1. 概述
本文档以 STM32F767 平台为例, 详细介绍 SylixOS 上 GPIO 模仿 I2C 总线的驱动开发流程
2. 初始化
GPIO 模仿的 I2C 总线的初始化, 实际上是 I2C 总线的 SDA 和 SCL 的 GPIO 管脚初始化初始化流程如图 2.1 所示
图 2.1 I2C 初始化流程图
代码实现, 如程序清单 2.1 所示 I2C 总线的 SDA 和 SCL 两个 GPIO 管脚的 GPIO 速度要设置成快速模式, 输出模式需要设置成推挽输出模式
程序清单 2.1 I2C 初始化代码
- /*
- * 申请 I2C 1 通道的 SCL 的 GPIO
- */
- if (ERROR_NONE != API_GpioRequest(I2C1_CHANNEL_SCL, I2C1_SCL_GPIO_NAME)) {
- return (PX_ERROR);
- }
- /*
- * 设置上拉
- */
- if (ERROR_NONE != API_GpioSetPull(I2C1_CHANNEL_SCL, GPIO_PUPD_PU)) {
- return (PX_ERROR);
- }
- /*
- * 设置为推挽输出模式, 且 GPIO 速度为快速
- */
- if (ERROR_NONE != API_GpioDirectionOutput(I2C1_CHANNEL_SCL,
- (GPIO_SPEED_SET |
- GPIO_OTYPE_SET |
- LW_GPIOF_INIT_HIGH))) {
- return (PX_ERROR);
- }
- /*
- * 申请 I2C 1 通道的 SDA 的 GPIO
- */
- if (ERROR_NONE != API_GpioRequest(I2C1_CHANNEL_SDA, I2C1_SDA_GPIO_NAME)) {
- return (PX_ERROR);
- }
- if (ERROR_NONE != API_GpioSetPull(I2C1_CHANNEL_SDA, GPIO_PUPD_PU)) {
- return (PX_ERROR);
- }
- if (ERROR_NONE != API_GpioDirectionOutput(I2C1_CHANNEL_SDA,
- (GPIO_SPEED_SET |
- GPIO_OTYPE_SET |
- LW_GPIOF_INIT_HIGH))) {
- return (PX_ERROR);
- }
3. 传输流程
GPIO 模拟 I2C 总线驱动和普通的 I2C 总线驱动的最大区别是普通的 I2C 总线驱动的数据传输只要将要传输的数据写入寄存器即可, 而 GPIO 模拟 I2C 总线驱动的数据传输是直接通过 GPIO 管脚将电平拉高拉低 (拉高是 1, 拉低是 0) 传输数据
3.1 写数据流程
如程序清单 3.1 所示, I2C 的写数据流程如下:
主设备发送开始信号;
主设备发送 7 位从设备地址和 1 位写操作位;
从设备发送应答信号;
主设备发送要写的 8 位从设备内部地址;
从设备发送应答信号;
主设备开始对从设备写操作;
主设备发送结束信号
程序清单 3.1 I2C 的写数据流程
- static INT __i2cXferWrite(UINT uiChannel, PLW_I2C_MESSAGE pI2cMsg, INT iLength) {
- INT iIndex;
- __i2cStart(uiChannel);
- /* 发送开始信号 */
- /*
- * 发送 7 位器件地址和 1 位写操作位
- */
- __i2cSendByte((pI2cMsg - >I2CMSG_usAddr & I2C_ADDR_MASK), uiChannel);
- if (__i2cWaitAck(uiChannel)) {
- /* 等待设备的 ACK 信号 */
- return (PX_ERROR);
- }
- /*
- * 发送要写的设备的内部地址
- */
- __i2cSendByte(((pI2cMsg - >I2CMSG_usAddr) & I2C_INTER_ADDR_MASK), uiChannel);
- if (__i2cWaitAck(uiChannel)) {
- /* 等待设备的 ACK 信号 */
- return (PX_ERROR);
- }
- for (iIndex = 0; iIndex < iLength; iIndex++) {
- __i2cSendByte( * (pI2cMsg - >I2CMSG_pucBuffer)++, uiChannel);
- /* 发送字节 */
- if (__i2cWaitAck(uiChannel)) {
- /* 等待设备的 ACK 信号 */
- return (PX_ERROR);
- }
- }
- __i2cStop(uiChannel);
- /* 产生一个停止信号 */
- udelay(I2C_WRITE_BYTE_DELAY);
- return (ERROR_NONE);
- }
3.2 读数据流程
如程序清单 3.2 所示, I2C 的读数据流程如下:
写模式, 主设备发送开始信号;
主设备发送 7 位从设备地址和 1 位写操作位;
从设备发送应答信号;
主设备发送要写的 8 位从设备内部地址;
从设备发送应答信号;
进入读取模式, 设备再次发送开始信号;
主设备发送 7 位从设备地址和 1 位读操作位;
从设备发送应答信号;
主设备开始对从设备读操作;
主设备发送结束信号
程序清单 3.2 I2C 读数据流程
- static INT __i2cXferRead (UINT uiChannel,
- PLW_I2C_MESSAGE pI2cMsg,
- INT iLength)
- {
- INT iIndex;
- __i2cStart(uiChannel); /* 发送开始信号 */
- /*
- * 发送 7 位器件地址和 1 位写操作位,(I2CMSG_usAddr 中的 9-15 位为器件地址)
- */
- __i2cSendByte(((pI2cMsg->I2CMSG_usAddr >> 8) & I2C_ADDR_MASK), uiChannel);
- if (__i2cWaitAck(uiChannel)) { /* 等待设备的 ACK 信号 */
- return (PX_ERROR);
- }
- /*
- * 发送要读的设备的内部地址
- */
- __i2cSendByte(((pI2cMsg->I2CMSG_usAddr) & I2C_INTER_ADDR_MASK), uiChannel);
- if (__i2cWaitAck(uiChannel)) { /* 等待设备的 ACK 信号 */
- return (PX_ERROR);
- }
- /*
- * 进入读取模式
- */
- __i2cStart(uiChannel); /* 发送开始信号 */
- /*
- * 发送 7 位器件地址和 1 位读操作位,(I2CMSG_usAddr 中的 8-15 位为器件地址和读写位)
- */
- __i2cSendByte(((pI2cMsg->I2CMSG_usAddr >> 8) &
- I2C_ADDR_MASK) |
- LW_I2C_M_RD, uiChannel);
- if (__i2cWaitAck(uiChannel)) { /* 等待设备的 ACK 信号 */
- return (PX_ERROR);
- }
- for (iIndex = 0; iIndex < iLength - 1; iIndex++) {
- /*
- * 读取设备发来的 1 个字节数据
- */
- *(pI2cMsg->I2CMSG_pucBuffer)++ = __i2cReadByte(I2C_ACK_SEND, uiChannel);
- }
- *(pI2cMsg->I2CMSG_pucBuffer) = __i2cReadByte(I2C_NACK_SEND, uiChannel);
- __i2cStop(uiChannel); /* 产生停止信号 */
- return (ERROR_NONE);
- }
来源: http://www.bubuko.com/infodetail-2508991.html