[AT32F407] 【AT-START-F407测评】+IIC读写与多字节读取异常钳住总线

[复制链接]
 楼主| tinnu 发表于 2021-2-15 23:51 | 显示全部楼层 |阅读模式
本帖最后由 tinnu 于 2021-2-15 23:55 编辑

使用的是轮询的例程例程修改的IIC读写程序。

(一)引脚
arduino引脚上的IIC是PB8、PB9,属于复用功能。
11.png 12.png 13.png
例程使用的PB6、PB7是默认的IIC1,复用映射需要额外使能复用功能:

  RCC_APB2PeriphClockCmd( RCC_APB2PERIPH_AFIO , ENABLE);
  GPIO_PinsRemapConfig(GPIO_Remap_I2C1,ENABLE);

(二)使能IIC功能
  1.     /* I2C configuration */
  2.     I2C_InitStructure.I2C_Mode = I2C_Mode_I2CDevice;
  3.     I2C_InitStructure.I2C_FmDutyCycle = I2C_FmDutyCycle_2_1;
  4.     I2C_InitStructure.I2C_OwnAddr1 = I2C_SLAVE_ADDRESS7;
  5.     I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  6.     I2C_InitStructure.I2C_AddrMode = I2C_AddrMode_7bit;
  7.     I2C_InitStructure.I2C_BitRate = I2C_SPEED;

  8.     /* I2C Peripheral Enable */
  9.     I2C_Cmd(I2C_PORT, ENABLE);

  10.     /* Apply I2C configuration after enabling it */
  11.     I2C_Init(I2C_PORT, &I2C_InitStructure);

对着例程照搬就行。

(三)器件
IIC器件采用MPU9250,一个九轴传感器,八位器件地址0xD0,WHO AM I位 0x75,数据为0x71.


(四)写IIC
写的方法很简单,例程照搬即可:

  1. I2C_StatusType I2C_Master_Transmit(I2C_Type* I2Cx, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
  2. {
  3.   /* Wait until BUSY flag is reset */
  4.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BUSYF, SET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  5.   {
  6.     return I2C_ERROR_STEP_1;
  7.   }

  8.   /* Disable Pos */
  9.   I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

  10.   /* Send START condition */
  11.   I2C_GenerateSTART(I2Cx, ENABLE);

  12.   /* Wait until SB flag is set */
  13.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_STARTF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  14.   {
  15.     /* Send STOP Condition */
  16.     I2C_GenerateSTOP(I2Cx, ENABLE);

  17.     return I2C_ERROR_STEP_2;
  18.   }

  19.   /* Send slave address for write */
  20.   I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Transmit);

  21.   /* Wait until ADDR flag is set */
  22.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_ADDRF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  23.   {
  24.     /* Send STOP Condition */
  25.     I2C_GenerateSTOP(I2Cx, ENABLE);

  26.     return I2C_ERROR_STEP_3;
  27.   }

  28.   /* Clear ADDR flag */
  29.   I2C_ClearADDRFlag(I2Cx);

  30.   while(Size > 0)
  31.   {
  32.     /* Wait until TDE flag is set */
  33.     if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_TDE, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  34.     {
  35.       /* Send STOP Condition */
  36.       I2C_GenerateSTOP(I2Cx, ENABLE);

  37.       return I2C_ERROR_STEP_4;
  38.     }

  39.     /* Write data to DR */
  40.     I2C_SendData(I2Cx, (*pData++));
  41.     Size--;
  42.   }

  43.   /* Wait until BTF flag is set */
  44.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  45.   {
  46.     /* Send STOP Condition */
  47.     I2C_GenerateSTOP(I2Cx, ENABLE);

  48.     return I2C_ERROR_STEP_5;
  49.   }

  50.   /* Send STOP Condition */
  51.   I2C_GenerateSTOP(I2Cx, ENABLE);

  52.   return I2C_OK;
  53. }

写入效果:
write.png

(五)读效果
雅特力的IIC在读IIC方面有一个比较特殊的处理,会预先读两位,可以通过配置CTL1寄存器第11位阻止预读取。
21.png
这里先不处理预读取的问题。
  1. I2C_StatusType I2C_Master_Rec2(I2C_Type* I2Cx, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
  2. {
  3.   /* Wait until BUSY flag is reset */
  4.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BUSYF, SET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  5.   {
  6.     return I2C_ERROR_STEP_1;
  7.   }

  8.   /* Disable Pos */
  9.   I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

  10.   /* Send START condition */
  11.   I2C_GenerateSTART(I2Cx, ENABLE);

  12.   /* Wait until SB flag is set */
  13.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_STARTF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  14.   {
  15.     /* Send STOP Condition */
  16.     I2C_GenerateSTOP(I2Cx, ENABLE);

  17.     return I2C_ERROR_STEP_2;
  18.   }

  19.   /* Send slave address for write */
  20.   I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Receive);

  21.   /* Wait until ADDR flag is set */
  22.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_ADDRF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  23.   {
  24.     /* Send STOP Condition */
  25.     I2C_GenerateSTOP(I2Cx, ENABLE);

  26.     return I2C_ERROR_STEP_3;
  27.   }

  28.   /* Clear ADDR flag */
  29.   I2C_ClearADDRFlag(I2Cx);

  30.   while(Size > 0)
  31.   {
  32.       /* Wait until TDE flag is set */
  33.           if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  34.           {
  35.               /* Send STOP Condition */
  36.               I2C_GenerateSTOP(I2Cx, ENABLE);
  37.               return I2C_ERROR_STEP_4;
  38.           }

  39.       /* Write data to DR */
  40.       (*pData++) = I2C_ReceiveData(I2Cx);
  41.       Size--;
  42.   }

  43.   if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  44.   {
  45.       /* Send STOP Condition */
  46.       I2C_GenerateSTOP(I2Cx, ENABLE);
  47.       return I2C_ERROR_STEP_5;
  48.   }
  49.   /* Send STOP Condition */
  50.   I2C_GenerateSTOP(I2Cx, ENABLE);

  51.   /* Disable Address Acknowledge */
  52.   I2C_AcknowledgeConfig(I2Cx, DISABLE);

  53.   return I2C_OK;
  54. }


读取一个位的效果:
I2C_Master_Rec2(I2C_PORT, I2C_SLAVE_ADDRESS7, tx_buf, 1, 1000);
32.png

但是读取多个位的时候,则会出现把总线钳住不释放的现象:
I2C_Master_Rec2(I2C_PORT, I2C_SLAVE_ADDRESS7, tx_buf, 2, 1000);
31.png
并且总线被钳住之后,硬件复位是无法释放总线的,也就是第一次出错后,在不断电的情况下,即便复位,下一次程序重新运行,重新初始化,依旧是总线忙的状态,无法写也无法读。

main.zip (2.24 KB, 下载次数: 2)

 楼主| tinnu 发表于 2021-2-19 19:27 | 显示全部楼层
本帖最后由 tinnu 于 2021-2-19 20:57 编辑

针对多字节发送钳住总线的问题,主要是没有规范地设置ack相关位导致的。
在addr(从机地址)发送完成后,不能简单地清除addr传输的相关位:

而是需要根据剩余字节数判断:

  1.   switch(Size){
  2.   case 1:
  3.       /* Disable Acknowledge */
  4.       I2C_AcknowledgeConfig(I2Cx, DISABLE);
  5.       /* Clear ADDR flag */
  6.       I2C_ClearADDRFlag(I2Cx);
  7.       /* Send STOP Condition */
  8.       I2C_GenerateSTOP(I2Cx, ENABLE);

  9.       if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  10.       {
  11.           /* Send STOP Condition */
  12.           I2C_GenerateSTOP(I2Cx, ENABLE);

  13.           return I2C_ERROR_STEP_4;
  14.       }
  15.       break;
  16.   case 2:
  17.       /* Enable Pos */
  18.       I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Next);
  19.       /* Disable Acknowledge */
  20.       I2C_AcknowledgeConfig(I2Cx, DISABLE);
  21.       /* Clear ADDR flag */
  22.       I2C_ClearADDRFlag(I2Cx);

  23.       if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  24.       {
  25.           /* Send STOP Condition */
  26.           I2C_GenerateSTOP(I2Cx, ENABLE);

  27.           return I2C_ERROR_STEP_4;
  28.       }
  29.       /* Send STOP Condition */
  30.       I2C_GenerateSTOP(I2Cx, ENABLE);
  31.       break;
  32.   default:
  33.       /* Enable Acknowledge */
  34.       I2C_AcknowledgeConfig(I2Cx, ENABLE);
  35.       /* Clear ADDR flag */
  36.       I2C_ClearADDRFlag(I2Cx);
  37.       break;
  38.   }

1-当传输的只有一个字节:
雅特力的IIC寄存器设置特点就是,最后读的一位不应答,因为一旦应答,雅特力的寄存器逻辑就认为需要继续读下一个位:
清除掉ACKEN位,清除掉addr标志位,然后立即设置停止位,这时候才等待本次传输完成。

2-当传输的有两个字节
置位第10位,POSEN位;即本次传输会应答,即默认再预读一位
清除掉ACKEN位,清除掉addr标志位
等待本位传输完成后,雅特力寄存器逻辑会立即开启下一位的预读;
然后再立即设置停止位,第二轮读取完成后,就会发送停止标志。

3-当传输的有三个字节
置位第10位,POSEN位;即本次传输会应答,即默认再预读一位
清除掉addr标志位

后面读取的过程也要额外判断几个状态:
  1.   while(Size > 0)
  2.   {
  3.       /* Wait until TDE flag is set */
  4.       if(Size>=3)
  5.           if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  6.           {
  7.               /* Send STOP Condition */
  8.               I2C_GenerateSTOP(I2Cx, ENABLE);

  9.               return I2C_ERROR_STEP_4;
  10.           }

  11.       /* Disable Address Acknowledge */
  12.       if(Size==3) I2C_AcknowledgeConfig(I2Cx, DISABLE);

  13.       /* Write data to DR */
  14.       (*pData++) = I2C_ReceiveData(I2Cx);
  15.       Size--;

  16.       if(Size==2)
  17.       {
  18.           if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  19.           {
  20.               /* Send STOP Condition */
  21.               I2C_GenerateSTOP(I2Cx, ENABLE);
  22.               return I2C_ERROR_STEP_5;
  23.           }
  24.           /* Send STOP Condition */
  25.           I2C_GenerateSTOP(I2Cx, ENABLE);
  26.       }
  27.   }

1-当剩余字节>=3,需要判断传输完成,剩余2、1个字节时,默认已经启动了停机流程了。
2-而=3传输并读取完成后,需要发送停机,因为雅特力默认会预读后两位
3-同样的=3传输完成后,在读取前需要置位第10位,POSEN位;即本次传输会应答

41.png

main2.zip (2.22 KB, 下载次数: 6)
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

72

帖子

0

粉丝
快速回复 返回顶部 返回列表