当前位置:   article > 正文

普冉PY32 作为I2C从机实现不固定长度收发数据的实现方式(基于HAL库例程实现)【STM32等MCU可参照实现】

py32

        在使用I2C外设HAL库或者LL库例程作为从机时进行通信时,如果我们使用发送/接收库函数对从机进行初始化,就要按照官方例程这样,先写一个接收初始化函数,并设定接收字节长度,接收buffer,查询从机I2C状态后,再执行一次发送初始化函数。

        如果我们非常清楚主机的发送时序,这样没有任何问题。但如果我们不知道主机何时开始发送,何时开始接收,也不知道主机会发送/接收多少个byte长度,这样的写法就会有问题。        

  1. /* I2C初始化 */
  2. I2cHandle.Instance = I2C; /* I2C */
  3. I2cHandle.Init.ClockSpeed = I2C_SPEEDCLOCK; /* I2C通讯速度 */
  4. I2cHandle.Init.DutyCycle = I2C_DUTYCYCLE; /* I2C占空比 */
  5. I2cHandle.Init.OwnAddress1 = I2C_ADDRESS; /* I2C地址 */
  6. I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; /* 禁止广播呼叫 */
  7. I2cHandle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; /* 允许时钟延长 */
  8. if (HAL_I2C_Init(&I2cHandle) != HAL_OK)
  9. {
  10. APP_ErrorHandler();
  11. }
  12. /*I2C从机中断方式接收*/
  13. while (HAL_I2C_Slave_Receive_IT(&I2cHandle, (uint8_t *)aRxBuffer, DARA_LENGTH) != HAL_OK)
  14. {
  15. APP_ErrorHandler();
  16. }
  17. /*判断当前I2C状态*/
  18. while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY);
  19. /*I2C从机中断方式发送*/
  20. while (HAL_I2C_Slave_Transmit_IT(&I2cHandle, (uint8_t *)aTxBuffer, DARA_LENGTH) != HAL_OK)
  21. {
  22. APP_ErrorHandler();
  23. }
  24. /*判断当前I2C状态*/
  25. while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY);

         为了探究更灵活的处理方式,我们可以从硬件I2C的中断逻辑入手,根据官方手册:EV表示在I2C通信过程中,从机会产生的中断。

         我们先定义从机I2C需要发送、接收的数据buffer,数据size等数据结构:

  1. /*---------IIC1---------------*/
  2. #define BUFFER_LENGTH 140
  3. uint8_t aTxBuffer[BUFFER_LENGTH];//发送缓存
  4. uint8_t aRxBuffer[BUFFER_LENGTH];//接收缓存
  5. uint8_t TxIndex = 0;//发送计数
  6. uint8_t RxIndex = 0;//接收计数
  7. uint8_t FlagRcvOk = 0;// 接收完成标志

         然后根据上图的I2C中断需要处理的节点,编写处理函数:

  1. void User_I2C_EV_IRQHandler(void)
  2. {
  3. static uint32_t SR1Register =0;
  4. static uint32_t SR2Register =0;
  5. SR1Register = I2C1->SR1;
  6. SR2Register = I2C1->SR2;
  7. /* I2C1是从机(MSL = 0) */
  8. /* 主机已发送地址,地址为被置位·(ADDR = 1: EV1(包括发送和接收)) */
  9. if((SR1Register & 0x0002) == 0x0002)
  10. {
  11. RxIndex=0;
  12. TxIndex=0;
  13. /* 验证传输方向,Read 时的方向,Slave 进入接收器模式 */
  14. if((uint32_t)(READ_BIT(SR2Register, I2C_SR2_TRA)) == 0x00000000U)//从机读
  15. {
  16. }
  17. else //从机写
  18. {
  19. }
  20. }
  21. /* 接收数据(RXNE = 1: EV2) */
  22. if((SR1Register & 0x0040) == 0x0040)
  23. {
  24. aRxBuffer[RxIndex++] = I2C1->DR;
  25. }
  26. /* 检测到停止条件(STOPF =1: EV4) */
  27. if((SR1Register & 0x0010) == 0x0010)
  28. {
  29. I2C1->CR1 |= 0x0001;
  30. FlagRcvOk = 1;
  31. }
  32. //无应答执行
  33. if((I2C1->SR1 & I2C_SR1_AF)==I2C_SR1_AF)
  34. {
  35. CLEAR_BIT(I2C1->SR1,I2C_SR1_AF);
  36. FlagRcvOk = 1;
  37. }
  38. /* 发送数据(TxE = 1: EV3、EV3-1) */
  39. if((SR1Register & 0x0080) == 0x0080)
  40. {
  41. I2C1->DR = aTxBuffer[TxIndex++];
  42. }
  43. }

        在中断函数里面调用处理函数,并做好发生错误中断时需要进行的处理:

  1. void I2C1_IRQHandler(void)
  2. {
  3. User_I2C_EV_IRQHandler();
  4. if((I2C1->SR1 & I2C_SR1_BERR)==I2C_SR1_BERR) //总线错误执行
  5. {
  6. CLEAR_BIT(I2C1->SR1,I2C_SR1_BERR);
  7. I2C_Init();
  8. }
  9. if((I2C1->SR1 & I2C_SR1_OVR)==I2C_SR1_OVR) //过载执行
  10. {
  11. CLEAR_BIT(I2C1->SR1,I2C_SR1_OVR);
  12. I2C_Init();
  13. }
  14. if((I2C1->SR1 & I2C_SR1_PECERR)==I2C_SR1_PECERR) //PEC执行
  15. {
  16. CLEAR_BIT(I2C1->SR1,I2C_SR1_PECERR);
  17. I2C_Init();
  18. }
  19. }

        贴一下HAL库的I2C配置函数,配置完开启中断就好了:

  1. void I2C_Init(void)
  2. {
  3. I2cHandle.Instance = I2C; //I2C
  4. I2cHandle.Init.ClockSpeed = I2C_SPEEDCLOCK; //I2C通讯速度
  5. I2cHandle.Init.DutyCycle = I2C_DUTYCYCLE; //I2C占空比
  6. I2cHandle.Init.OwnAddress1 = I2C_ADDRESS; //I2C地址
  7. I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; //禁止广播呼叫
  8. I2cHandle.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE; //允许时钟延长
  9. if (HAL_I2C_DeInit(&I2cHandle) != HAL_OK) //I2C初始化
  10. {
  11. Error_Handler();
  12. }
  13. __HAL_RCC_I2C_CLK_DISABLE();
  14. if (HAL_I2C_Init(&I2cHandle) != HAL_OK) //I2C初始化
  15. {
  16. Error_Handler();
  17. }
  18. SET_BIT(I2C1->CR1, I2C_CR1_ACK);//使能应答
  19. SET_BIT(I2C1->CR2, I2C_IT_EVT);
  20. SET_BIT(I2C1->CR2, I2C_IT_BUF);
  21. SET_BIT(I2C1->CR2, I2C_IT_ERR);
  22. }

        最后在main函数里面更新从机要发送的数据,直接判断完成标志后,对buffer进行数据赋值就好了:

  1. int main(void)
  2. {
  3. unsigned char i;
  4. HAL_Init();
  5. I2C_Init();//I2C初始化
  6. for(i = 0;i < DARA_LENGTH;i++)
  7. {
  8. aTxBuffer[i]=i;
  9. }
  10. while(1)
  11. {
  12. if(FlagRcvOk)
  13. {
  14. FlagRcvOk = 0;
  15. for(i = 0;i < DARA_LENGTH;i++)
  16. {
  17. aTxBuffer[i]++;
  18. }
  19. }
  20. }
  21. }

        完成这些操作,这时候就可以让PY32作为从机进行I2C通信并自由应答了!

        程序写完用逻辑分析器看一下波形,非常nice:

        完结撒花✿✿ヽ(°▽°)ノ✿

>>>【点击进入,普冉PY32仿真烧录工具】​​icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/673851365

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/602551
推荐阅读
相关标签
  

闽ICP备14008679号