在《嵌入式硬件通信接口协议-SPI(二)分层架构设计模拟接口》一文中,我们已经详细介绍了SPI接口的分层架构设计思路。现在,我们将基于同样的方法,来设计IIC(Inter-Integrated Circuit)接口的BSP(Board Support Package)层代码模块。
本文将主要分为两个部分:IIC接口管理和IIC时序管理。
IIC 接口管理
我们的目标是允许一个工程中使用多个IIC接口。在后期扩展时,这可以通过简单地定义宏来实现,而不需要考虑复杂的数据结构或内存分配。这意味着我们可以通过头文件中的宏定义来指定每个IIC接口的序号、端口和引脚。
例如:
#define IIC_1 1
#define IIC1_PORT_SCL PORTB
#define IIC1_PIN_SCL 6
#define IIC1_PORT_SDA PORTA
#define IIC1_PIN_SDA 2
在这个例子中,我们定义了第一个IIC接口,并指定了其对应的端口、引脚以及其他必要参数。这样做可以方便地添加新的IIC接口,只需根据现有示例增加相应的宏定义即可。
IIC 时序管理
在上一篇文章《嵌入式硬件通信协议-IIC(一):协议基础》中,我们了解到了IIC时序特征,如起始标志、结束标志和数据输出等信号特征。现在,我们将按照这些信号特征,逐步实现它们。
由于这部分内容在互联网上已经有很多相关实践案例,可以从开源社区、百度百科或者技术论坛找到大量关于如何实现这些时序功能的代码示例。不过,在这里我们还是会尝试“造轮子”,以提高项目中的扩展性和应用能力,同时优化整体框架结构。
起始标志
为了发送起始标志信号,我们首先确保SDA管脚为输出,然后设置SCL管脚为低电平,以便开始传输过程。具体代码如下:
void iic_start_signal(uint8_t iic_n) {
// 设置SDA管脚为输出模式并拉低电平作为起始条件。
iic_set_io(iic_n, 0);
}
结束标志
结束标志信号与起始标志类似,但是在最后阶段释放SCL管脚,使之回到高电平状态,从而完成整个通信过程:
void iic_stop_signal(uint8_t iic_n) {
// 设置SCL管脚为高电平作为停止条件。
iic_set_io(iic_n, 1);
}
数据输出
对于数据发送,关键点是当SCL处于低电平状态时才允许改变SDA上的数据位。当SCL变成高电平后,则必须保持SDA稳定,不进行任何变化。此外,每次发送一个bit之前,都要先移位操作以准备下一个bit进行发送:
void data_output(uint8_t data, uint8_t bit_num, uint8_t iic_n) {
while (bit_num--) {
if (data & (0x01 << bit_num)) { // 如果该位是'1'
// SDA设置为高电平表示'1'
set_pin_high(IICSdaPin[i]);
} else { // 如果该位是'0'
// SDA设置为低电平表示'0'
set_pin_low(IICSdaPin[i]);
}
delayMicroseconds(10); // 延迟10微秒
if ((i++ % IICSclCount == 0 || !iicscl_bit_count()) && scl_state == LOW) { // 检查是否到达下一次SCL周期,并且当前状态应该是LOW才能写入新值。
scl_state = HIGH;
delayMicroseconds(100); // 等待100微秒
scl_state = LOW;
delayMicroseconds(10); // 延迟10微秒
scl_state = HIGH;
delayMicroseconds(100); # 等待100微秒
scl_state = LOW;
delayMicroseconds(4.5 * TIME_PER_BIT - TIME_PER_BIT/2 + TIME_DELAY + TOLERANCE_TIME + DUTY_CYCLE_TIME);
continue;# 跳过循环继续执行下一次while循环
break;# 出循环
return;# 返回函数
continue;# 跳过循环继续执行下一次while循环
return;
continue;
break;
return;
continue;
return;
return
}else{
}if(bit_num>7){break;}else{i++;}return;i++;
// 在主机向从机发出的每个字节之后都要确认是否收到正确信息,如果没有回应则重新传输此字节。
// 主机发出ACK/NACK响应
}
// 应答时间延迟时间通常设定得比主机较长一些,以避免误判
function ackTimeDelay() {
delay(TIME_DELAY);
}