CubieBoard中文论坛

 找回密码
 立即注册
搜索
热搜: unable
查看: 10235|回复: 0

I2C进阶4

[复制链接]
发表于 2014-9-25 11:34:18 | 显示全部楼层 |阅读模式
到此为止我们会带来几个疑问:第一, aw_twi_enable_irq和 aw_twi_start具体怎么实现的?第二,等待队列在何时被唤醒呢?
首先我们先来研究第一个问题,aw_twi_enable_irq实现开中断如下:

static inline void aw_twi_enable_irq(void *base_addr)

{

unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
/*

* 1 when enable irq for next operation, set intflag to 1 to prevent to clear it by a mistake

* (intflag bit is write-0-to-clear bit)

* 2 Similarly, mask startbit and stopbit to prevent to set it twice by a mistake

* (start bit and stop bit are self-clear-to-0 bits)

*/

reg_val |= (TWI_CTL_INTEN | TWI_CTL_INTFLG);

reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP);

writel(reg_val, base_addr + TWI_CTL_REG);

}
/* function */

static int aw_twi_start(void *base_addr)

{

unsigned int timeout = 0xff;
aw_twi_set_start(base_addr); /* trigger start signal, the start bit will be cleared automatically */

while((1 == aw_twi_get_start(base_addr))&&(--timeout));

/* get start bit status, poll if start signal is sent */

if(timeout == 0) {

i2c_dbg("START can't sendout!\n");

return AWXX_I2C_FAIL;

}
return AWXX_I2C_OK;

}

总结下我们这个 aw_twi_start函数吧,这个函数主要做了两件事,第一使能ACK信号。第二,将从机地址和读写方式控制字写入待IICDS中,由IICSTAT发送S信号,启动发送从机地址。
嗯,到现在为止我们已经把前面提出的第一个问题解决了,该轮到解决第二个问题了,s3c24xx_i2c_doxfer中的等待队列何时被唤醒呢?其实分析到现在我们发现s3c24xx_i2c_doxfer调用3c24xx_i2c_message_start只是发送了一个从机的地址。真正的数据传输在哪里呢?其实真正的数据传输我们放在了中断处理函数中实现的。当数据准备好发送时,将产生中断,并调用事先注册的中断处理函数s3c24xx_i2c_irq进行数据传输。中断的产生其实有3种情况,第一,总线仲裁失败。第二,当总线还处于空闲状态,因为一些错误操作等原因,意外进入了中断处理函数。第三,收发完一个字节的数据,或者当收发到的I2C设备地址信息吻合。行,那我们先来看看s3c24xx_i2c_irq到底怎么来传输数据的吧。
static irqreturn_t i2c_sunxi_handler(int this_irq, void * dev_id)

{

struct sunxi_i2c *i2c = (struct sunxi_i2c *)dev_id;

int ret = AWXX_I2C_FAIL;

if(!aw_twi_query_irq_flag(i2c->base_addr)) { //获取中断标志位,成功返回标志位

pr_warning("unknown interrupt!");

return ret;

//return IRQ_HANDLED;

}

/* disable irq */

aw_twi_disable_irq(i2c->base_addr);

//twi core process

ret = i2c_sunxi_core_process(i2c);

/* enable irq only when twi is transfering, otherwise,disable irq 当twi传输数据时候进入中断,否则。关闭终端*/

if(i2c->status != I2C_XFER_IDLE) {

aw_twi_enable_irq(i2c->base_addr);

}

return IRQ_HANDLED;

}

我们发现其实中断处理函数s3c24xx_i2c_irq中真正传输数据的函数是 i2s_s3c_irq_nextbyte。走了这么久,其实i2s_s3c_irq_nextbyte才是真正的传输数据的核心函数,那我们赶紧来看看  i2s_s3c_irq_nextbyte吧。

ret = i2c_sunxi_core_process(i2c);

static int i2c_sunxi_core_process(struct sunxi_i2c *i2c)

{

void *base_addr = i2c->base_addr;

int ret = AWXX_I2C_OK;

int err_code = 0;

unsigned char state = 0;

unsigned char tmp = 0;

state = aw_twi_query_irq_status(base_addr);

#ifdef CONFIG_SUNXI_IIC_PRINT_TRANSFER_INFO

if(i2c->bus_num == CONFIG_SUNXI_IIC_PRINT_TRANSFER_INFO_WITH_BUS_NUM){

i2c_dbg("sunxi_i2c->bus_num = %d, sunxi_i2c->msg->addr = (0x%x) state = (0x%x)\n", \

i2c->bus_num, i2c->msg->addr, state);

}

#endif

if(i2c->msg == NULL) {

printk("i2c->msg is NULL, err_code = 0xfe\n");

err_code = 0xfe;

goto msg_null;

}

switch(state) {

case 0xf8: /* On reset or stop the bus is idle, use only at poll method */

err_code = 0xf8;

goto err_out;

case 0x08: /* A START condition has been transmitted */ 发送开始信号S

case 0x10: /* A repeated start condition has been transmitted */

i2c_sunxi_addr_byte(i2c);/* send slave address */

break;

//当没有接收到ACK应答信号,说明I2C设备不存在,应停止总线工作

case 0xd8: /* second addr has transmitted, ACK not received! */

case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */

err_code = 0x20;

goto err_out;

/* 0x18 Address+Write bit transmitted, ACK received */

case 0x18: /* SLA+W has been transmitted; ACK has been received */

/* if any, send second part of 10 bits addr */

if(i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) {

tmp = i2c->msg[i2c->msg_idx].addr & 0xff; /* the remaining 8 bits of address */

aw_twi_put_byte(base_addr, &tmp); /* case 0xd0: */

break;

}

/* for 7 bit addr, then directly send data byte--case 0xd0: */

case 0xd0: /* second addr has transmitted,ACK received! */

case 0x28: /* Data byte in DATA REG has been transmitted; ACK has been received */

/* after send register address then START send write data 。。。开始发送写数据*/

if(i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) {

aw_twi_put_byte(base_addr, &(i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]));

i2c->msg_ptr++;

break;

}

i2c->msg_idx++; /* the other msg */   //表示已经传输完1条消息

i2c->msg_ptr = 0; //下一条消息字符串的首地址置0

if (i2c->msg_idx == i2c->msg_num) {

err_code = AWXX_I2C_OK;/* Success,wakeup */

goto ok_out;

}

else if(i2c->msg_idx < i2c->msg_num) {/* for restart pattern */

ret = aw_twi_restart(base_addr);/* read spec, two msgs */

if(ret == AWXX_I2C_FAIL) {

err_code = AWXX_I2C_SFAIL;

goto err_out;/* START can't sendout */

}

}

else {

err_code = AWXX_I2C_FAIL;

goto err_out;

}

break;

case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */

err_code = 0x30;//err,wakeup the thread

goto err_out;

case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */

err_code = 0x38;//err,wakeup the thread

goto err_out;

case 0x40: /* SLA+R has been transmitted; ACK has been received */

/* with Restart,needn't to send second part of 10 bits addr,refer-"I2C-SPEC v2.1" */

/* enable A_ACK need it(receive data len) more than 1. */

if(i2c->msg[i2c->msg_idx].len > 1) {

/* send register addr complete,then enable the A_ACK and get ready for receiving data */

aw_twi_enable_ack(base_addr);

aw_twi_clear_irq_flag(base_addr);/* jump to case 0x50 */

}

else if(i2c->msg[i2c->msg_idx].len == 1) {

aw_twi_clear_irq_flag(base_addr);/* jump to case 0x58 */

}

break;

case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */

err_code = 0x48;//err,wakeup the thread

goto err_out;

// t调用aw_twi_get_byte读取数据。从读取数据,将读取到的数据放入缓存区, msg_ptr++直到当前消息传输完毕

case 0x50: /* Data bytes has been received; ACK has been transmitted 数据位以及接收到,ACK没有传送到 */

/* receive first data byte */

if (i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) {

/* more than 2 bytes, the last byte need not to send ACK */

if( (i2c->msg_ptr + 2) == i2c->msg[i2c->msg_idx].len ) {

aw_twi_disable_ack(base_addr);/* last byte no ACK */

}

/* get data then clear flag,then next data comming */

aw_twi_get_byte(base_addr, &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]);

i2c->msg_ptr++;

break;

}

/* err process, the last byte should be @case 0x58 */

err_code = AWXX_I2C_FAIL;/* err, wakeup */

goto err_out;

case 0x58: /* Data byte has been received; NOT ACK has been transmitted 数据位读到了,没有ACK被传送到*/

/* received the last byte */

//判断是当前处理的消息是否是最后一个消息

if ( i2c->msg_ptr == i2c->msg[i2c->msg_idx].len - 1 ) {

aw_twi_get_last_byte(base_addr, &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]);

i2c->msg_idx++;

i2c->msg_ptr = 0;

if (i2c->msg_idx == i2c->msg_num) {

err_code = AWXX_I2C_OK; // succeed,wakeup the thread

goto ok_out;

}

else if(i2c->msg_idx < i2c->msg_num) {

/* repeat start */

ret = aw_twi_restart(base_addr);

if(ret == AWXX_I2C_FAIL) {/* START fail */

err_code = AWXX_I2C_SFAIL;

goto err_out;

}

break;

}

}

else {

err_code = 0x58;

goto err_out;

}

case 0x00: /* Bus error during master or slave mode due to illegal level condition */

err_code = 0xff;

goto err_out;

default:

err_code = state;

goto err_out;

}

i2c->debug_state = state;/* just for debug */

return ret;

ok_out:

err_out:

if(AWXX_I2C_FAIL == aw_twi_stop(base_addr)) {

i2c_dbg("STOP failed!\n");

}

msg_null:

ret = i2c_sunxi_xfer_complete(i2c, err_code);/* wake up */

i2c->debug_state = state;/* just for debug */

return ret;

}

我们终于把这个庞大的搞定了,在这里需要说明几点,第一,消息分为第一条消息,第二条消息,第三条消息等,总共有msg_num条消息,每发送完一个消息,会msg_idx++。每条消息发送完还需要调用

aw_twi_restart进行发送新的起始信号S。第二,第i条消息是一个字符串,按照一个字节一个字节的形式发送,由一个指针msg_ptr指向这个字符串的待发送字节的地址。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|粤ICP备13051116号|cubie.cc---深刻的嵌入式技术讨论社区

GMT+8, 2024-3-28 20:37 , Processed in 0.020690 second(s), 15 queries .

Powered by Discuz! X3.4

© 2001-2012 Comsenz Inc. | Style by Coxxs

返回顶部