目录
1. I2C概括
有关PKE8720DF-C13-F10开发板的介绍,请参考这一篇文章《快速上手PKE8720DF-C13-F10开发板》。
PKE8720DF-C13-F10中提供了一个I2C端口,用于传输和接收外部传感器的数据。
I2C总线有下面几个属性:
- 支持标准模式、快速模式、高速模式、超快速模式四种,但这里PKE8720DF-C13-F10支持前面两种,即标准模式(100kbps)和快速模式(400kbps)。
- 支持master和slave两种方式,这里的master也可以有多个,当有多个master时,由总线裁决方案来决定哪个master活动。
- 支持7位寻址和10位寻址两种。
- 最长为16字节的发送/接收缓存。
- 支持TX/RX DMA。
2. 硬件说明
2.1 连线说明
I2C用到了SDA线和SCL线,其中SDA表示串行数据线(serial data line),SCL表示串行时钟线(serial clock data)。在硬件连接时,两条线都要接上上拉电阻。
上拉电阻的具体计算比较复杂,细节可参考“I2C Bus Pullup Resistor Calculation”。
连线时,可以通过杜邦线将master和slave的板子连接起来,SDA连SDA,SCL连SCL。
在PKE8720DF-C13-F10的引脚图中,我们可以看到,SDA默认是PA26,SCL默认是PA25。
2.2 启动和停止条件
在主设备决定开始通信时,需要先发送开始信号:
SDA、SCL初始化默认是高电平(因为被拉高),然后SDA先降低,紧接着SCL再降低,然后就表示通信开始。
由于I2C支持多master、多slave,在一个master决定发起通信前,要先检查发起通信前,SDA当前是否处于高电平。
当主设备决定结束通信时,要发送停止信号:
当SDA从低电平拉高,紧接着SCL从高电平拉低时,表示停止通信。
3. 软件说明
在开始使用前,我们做下面的准备。
3.1 ImageTool
用于download编译好的image到板子上。
3.2 Linux/Windows开发环境
其源代码获取路径,和代码编译流程,均可参考《快速上手PKE8720DF-C13-F10开发板》。
3.3 Tera Term
可在“Tera Term Download”下载该工具。其作用是用于查看master和slave的收发数据。
4. 代码介绍
4.1 Master
将两个PKE8720DF-C13-F10开发板中的一个当作master处理,另一个当作slave处理。此时,两个板子上运行的是不同模式的程序。
对master而言,我们查看如下目录下的代码:
vim ${ambd_sdk}/project/realtek_amebaD_va0_example/example_sources/I2C/raw/i2c_dual_master/src/main.c
在这个文件里,我们看到有这一行:
#define I2C_MASTER_DEVICE
表示这里是处理的master方式。
另外还有它要发送/接收的slave的设备地址:
#define MBED_I2C_SLAVE_ADDR0 0x23
这里是写死的代码,只是测试使用,表示slave地址为0x23,因为这里处理的是一对一的master和slave。实际使用过程中,可能是一对多或者多对多,slave设备可能有多个,每个有自己的地址,那么,可以维护一个slave的地址列表,从中进行匹配。
#define MBED_I2C_BUS_CLK 100000
这个表示当前选择的是100kbps的标准模式。
#define MBED_I2C_SLV_SDA PA_26
#define MBED_I2C_SLV_SCL PA_25
表示使用默认的PA26和PA25作为SDA线和SCL线。
void i2c_Init(i2c_t *obj, PinName sda, PinName scl)
{
// ......
PAD_PullCtrl(sda, GPIO_PuPd_UP);
PAD_PullCtrl(scl, GPIO_PuPd_UP);
// ......
}
这里表示,SDA、SCL已经进行了拉高处理,不需要再额外接上拉电阻。
uint8_t i2cdatasrc[I2C_DATA_LENGTH];
uint8_t i2cdatadst[I2C_DATA_LENGTH];
uint8_t i2cdatardsrc[I2C_DATA_LENGTH];
uint8_t i2cdatarddst[I2C_DATA_LENGTH];
这里的几个全局数组,i2cdatasrc和i2cdatadst分别表示write的源数据和目的数据,i2cdatardsrc和i2cdatarddst表示read的源数据和目的数据。
在main()函数中,有对这几个数组进行初始化的操作,即将它们赋值为自己想要的数值,方便后面检查的时候,查看是否符合最开始时初始化设置的数值。
void i2c_master_rx_check(void)
{
// ......
if (i2cdatarddst[i2clocalcnt] != i2cdatardsrc[i2clocalcnt]) {
// ......
}
为了检查master收到的data是否符合预期,我们可以将下面这一行注释打开,即可以将实际收到的data内容打印出来。
void i2c_master_rx_check(void)
{
// ......
for (i2clocalcnt = 0; i2clocalcnt < I2C_DATA_LENGTH; i2clocalcnt+=2) {
DiagPrintf("i2c data: %02x \t %02x\n",i2cdatarddst[i2clocalcnt],i2cdatarddst[i2clocalcnt+1]);
}
// ......
}
这里就是master在接收data时,检查接收的数据,是否符合预期。
将这个main.c复制到src_hp路径下,覆盖里面的main.c。
cp -f main.c ../../../../../src/src_hp/
然后编译生成三个image文件,烧录到master板子中。
4.2 Slave
对slave而言,我们查看如下目录下的代码:
vim ${ambd_sdk}/project/realtek_amebaD_va0_example/example_sources/I2C/raw/i2c_dual_slave/src/main.c
我们可以用vimdiff或其它比较工具,将这个main.c文件,和上面master模式对应得main.c文件进行比较,可以发现,它里面的这一行是没有定义的:
// #define I2C_MASTER_DEVICE
也就是说,这一项关闭之后,下面的代码中,所有执行到编译选项“#ifdef I2C_MASTER_DEVICE”、“#else”的地方,都会执行slave的分支。
其余的几个定义,则是一样的。
#define MBED_I2C_SLAVE_ADDR0 0x23
这里表示当前这个slave设备的地址默认为0x23,是写死的。真实使用时,需根据实际情况进行调整。
#define MBED_I2C_BUS_CLK 100000
这个表示当前选择的是100kbps的标准模式,和master保持一致。
#define MBED_I2C_SLV_SDA PA_26
#define MBED_I2C_SLV_SCL PA_25
表示使用默认的PA26和PA25作为SDA线和SCL线。
void i2c_Init(i2c_t *obj, PinName sda, PinName scl)
{
// ......
PAD_PullCtrl(sda, GPIO_PuPd_UP);
PAD_PullCtrl(scl, GPIO_PuPd_UP);
// ......
}
这里表示,SDA、SCL已经进行了拉高处理,不需要再额外接上拉电阻。
与master一样,为了检查slave收到的data是否符合预期,我们可以将下面这一行注释打开,即可以将实际收到的data内容打印出来。
void i2c_slave_rx_check(void)
{
// ......
for (i2clocalcnt = 0; i2clocalcnt < I2C_DATA_LENGTH; i2clocalcnt+=2) {
DiagPrintf("i2c data: %02x \t %02x\n",i2cdatadst[i2clocalcnt],i2cdatadst[i2clocalcnt+1]);
}
// ......
}
将这个main.c复制到src_hp路径下,覆盖里面的main.c。
cp -f main.c ../../../../../src/src_hp/
然后编译生成三个image文件,烧录到slave板子中。
5. 实验结果
由于这里测试的是一对一的master、slave收发数据,所以将两者的SDA直接相连,SCL直接相连即可。
然后,将它们一起通过type-C USB线连接到PC机上,查看它们各自的port口。
打开两个Tera Term工具窗口,分别开启两个port的窗口,波特率选择115200。然后,先按下slave的reset键,再按下master的reset键。可以看到,在界面上,打印出来的data内容,和两个main.c里面设置的全局数组中的内容,是一致的。
当然,测试者也可以根据自己的偏好,修改几个全局数组里面的data内容,查看最终master和slave实际接收的data,是否和自己填写的内容一致。