Start信号之后,发出设备地址,在第9个时钟就会产生一个中断,我们根据i2c的流程图来编写中断程序。
每传输完一个数据将产生一个中断,I2C操作的主体在中断服务程序,它可以分为两部分:写操作,读操作。
完整code如下:
static p_i2c_msg p_cur_msg; int isLastData(void) { if (p_cur_msg->cnt_transferred == p_cur_msg->len - 1) return 1; /* 正要开始传输最后一个数据 */ else return 0; } void resume_iic_with_ack(void) { unsigned int iiccon = IICCON; iiccon |= (1<<7); /* 回应ACK */ iiccon &= ~(1<<4); /* 恢复IIC操作 */ IICCON = iiccon; } void resume_iic_without_ack(void) { unsigned int iiccon = IICCON; iiccon &= ~((1<<7) | (1<<4)); /* 不回应ACK, 恢复IIC操作 */ IICCON = iiccon; } void i2c_interrupt_func(int irq) { int index; unsigned int iicstat = IICSTAT; unsigned int iiccon; //printf("i2c_interrupt_func! flags = %d ", p_cur_msg->flags); p_cur_msg->cnt_transferred++; /* 每传输完一个数据将产生一个中断 */ /* 对于每次传输, 第1个中断是"已经发出了设备地址" */ if (p_cur_msg->flags == 0) /* write */ { /* 对于第1个中断, 它是发送出设备地址后产生的 * 需要判断是否有ACK * 有ACK : 设备存在 * 无ACK : 无设备, 出错, 直接结束传输 */ if (p_cur_msg->cnt_transferred == 0) /* 第1次中断 */ { if (iicstat & (1<<0)) /*iicstat [0] == 1表示no ack*/ { /* no ack */ /* 停止传输 */ IICSTAT = 0xd0; IICCON &= ~(1<<4); //clear pending bit p_cur_msg->err = -1; printf("tx err, no ack "); delay(1000); return; } } if (p_cur_msg->cnt_transferred < p_cur_msg->len) { /* 对于其他中断, 要继续发送下一个数据 */ IICDS = p_cur_msg->buf[p_cur_msg->cnt_transferred]; IICCON &= ~(1<<4);//clear pending bit } else { /* 停止传输 */ IICSTAT = 0xd0; IICCON &= ~(1<<4); delay(1000); } } else /* read */ { /* 对于第1个中断, 它是发送出设备地址后产生的 * 需要判断是否有ACK * 有ACK : 设备存在, 恢复I2C传输, 这样在下一个中断才可以得到第1个数据 * 无ACK : 无设备, 出错, 直接结束传输 */ if (p_cur_msg->cnt_transferred == 0) /* 第1次中断 */ { if (iicstat & (1<<0)) { /* no ack */ /* 停止传输 */ IICSTAT = 0x90; IICCON &= ~(1<<4); //clear pending bit p_cur_msg->err = -1; printf("rx err, no ack "); delay(1000); return; } else /* ack */ { /* 如果是最后一个数据, 启动传输时要设置为不回应ACK */ /* 恢复I2C传输 */ if (isLastData()) { resume_iic_without_ack(); } else { resume_iic_with_ack(); } return; } } /* 非第1个中断, 表示得到了一个新数据 * 从IICDS读出、保存 */ if (p_cur_msg->cnt_transferred < p_cur_msg->len) { index = p_cur_msg->cnt_transferred - 1; p_cur_msg->buf[index] = IICDS; /* 如果是最后一个数据, 启动传输时要设置为不回应ACK */ /* 恢复I2C传输 */ if (isLastData()) { resume_iic_without_ack(); } else { resume_iic_with_ack(); } } else { /* 发出停止信号 */ IICSTAT = 0x90; IICCON &= ~(1<<4); delay(1000); } }
View Code
写操作:
if (p_cur_msg->flags == 0) /* write */ { /* 对于第1个中断, 它是发送出设备地址后产生的 * 需要判断是否有ACK * 有ACK : 设备存在 * 无ACK : 无设备, 出错, 直接结束传输 */ if (p_cur_msg->cnt_transferred == 0) /* 第1次中断 */ { if (iicstat & (1<<0)) /*iicstat [0] == 1表示no ack*/ { /* no ack */ /* 停止传输 */ IICSTAT = 0xd0; IICCON &= ~(1<<4); //clear pending bit p_cur_msg->err = -1; printf("tx err, no ack "); delay(1000); return; } } if (p_cur_msg->cnt_transferred < p_cur_msg->len) { /* 对于其他中断, 要继续发送下一个数据 */ IICDS = p_cur_msg->buf[p_cur_msg->cnt_transferred]; IICCON &= ~(1<<4);//clear pending bit } else { /* 停止传输 */ IICSTAT = 0xd0; IICCON &= ~(1<<4); delay(1000); } }
1).p_cur_msg->cnt_transferred初始值为-1(do_master_tx启动时设置)。
2).p_cur_msg->cnt_transferred == 0表示是第一次传输数据完后产生的中断,即发送从设备地址产生的中断。
3).iicstat & (1<<0)表示主机没有接受到ACK信号(即发出的设备地址不存在),需要停止传输。
4).IICSTAT = 0xd0置IICSTAT寄存器的[5]写为0,产生P信号。但是由于这时IICCON[4]仍为1,P信号没有实际发出,当执行IICCON &= ~(1<<4);清除IICCON[4]后,P信号才真正发出。
5).等待一段时间,确保P信号已经发送完毕。
1).假如if (p_cur_msg->cnt_transferred < p_cur_msg->len)条件成立,表示数据还没有发送完毕,需要继续发送数据。
2).执行IICDS = p_cur_msg->buf[p_cur_msg->cnt_transferred]把要发送的数据写入到IICDS寄存器中,经过执行IICCON &= ~(1<<4);清除中断标志后后,紧接着就自动把数据发送出去了,这将触发下一个中断。
3).如果条件不成立表示数据传输完毕,发出P信号,停止数据的传输。
读操作:
else /* read */ { /* 对于第1个中断, 它是发送出设备地址后产生的 * 需要判断是否有ACK * 有ACK : 设备存在, 恢复I2C传输, 这样在下一个中断才可以得到第1个数据 * 无ACK : 无设备, 出错, 直接结束传输 */ if (p_cur_msg->cnt_transferred == 0) /* 第1次中断 */ { if (iicstat & (1<<0)) { /* no ack */ /* 停止传输 */ IICSTAT = 0x90; IICCON &= ~(1<<4); //clear pending bit p_cur_msg->err = -1; printf("rx err, no ack "); delay(1000); return; } else /* ack */ { /* 如果是最后一个数据, 启动传输时要设置为不回应ACK */ /* 恢复I2C传输 */ if (isLastData()) { resume_iic_without_ack(); } else { resume_iic_with_ack(); } return; } } /* 非第1个中断, 表示得到了一个新数据 * 从IICDS读出、保存 */ if (p_cur_msg->cnt_transferred < p_cur_msg->len) { index = p_cur_msg->cnt_transferred - 1; p_cur_msg->buf[index] = IICDS; /* 如果是最后一个数据, 启动传输时要设置为不回应ACK */ /* 恢复I2C传输 */ if (isLastData()) { resume_iic_without_ack(); } else { resume_iic_with_ack(); } } else { /* 发出停止信号 */ IICSTAT = 0x90; IICCON &= ~(1<<4); delay(1000); } }
测试:
void do_write_at24cxx(void) { unsigned int addr; unsigned char str[100]; int err; /* 获得地址 */ printf("Enter the address of sector to write: "); addr = get_uint(); if (addr > 256) { printf("address > 256, error! "); return; } printf("Enter the string to write: "); gets(str); printf("writing ... "); err = at24cxx_write(addr, str, strlen(str)+1); printf("at24cxx_write ret = %d ", err); } void do_read_at24cxx(void) { unsigned int addr; int i, j; unsigned char c; unsigned char data[100]; unsigned char str[16]; int len; int err; int cnt = 0; /* 获得地址 */ printf("Enter the address to read: "); addr = get_uint(); if (addr > 256) { printf("address > 256, error! "); return; } /* 获得长度 */ printf("Enter the length to read: "); len = get_int(); err = at24cxx_read(addr, data, len); printf("at24cxx_read ret = %d ", err); printf("Data : "); /* 长度固定为64 */ for (i = 0; i < 4; i++) { /* 每行打印16个数据 */ for (j = 0; j < 16; j++) { /* 先打印数值 */ c = data[cnt++]; str[j] = c; printf("%02x ", c); } printf(" ; "); for (j = 0; j < 16; j++) { /* 后打印字符 */ if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */ putchar('.'); else putchar(str[j]); } printf(" "); } } void i2c_test(void) { char c; /* 初始化 */ i2c_init(); while (1) { /* 打印菜单, 供我们选择测试内容 */ printf("[w] Write at24cxx "); printf("[r] Read at24cxx "); printf("[q] quit "); printf("Enter selection: "); c = getchar(); printf("%c ", c); /* 测试内容: * 3. 编写某个地址 * 4. 读某个地址 */ switch (c) { case 'q': case 'Q': return; break; case 'w': case 'W': do_write_at24cxx(); break; case 'r': case 'R': do_read_at24cxx(); break; default: break; } } }