找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3490|回复: 1
打印 上一主题 下一主题
收起左侧

STM32 IIC 硬件通信解决关键点,已验证通过

[复制链接]
跳转到指定楼层
楼主
ID:102668 发表于 2016-1-11 04:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 51hei社区 于 2016-1-11 04:55 编辑


本文已假设你了解了STM32的IIC的基础知识,也大概了解了IIC会出现问题。
本文只简单介绍两个具体例子,其他依照处理即可。
准备物品:一个逻辑分析仪,这样才能可靠了解I2C在哪里出错了。某宝上很便宜,100以内。
例子:1BYTE接受和2BYTE接收。
解决办法有两种,意思都一样:
1:暂时提升权限到“最高”,在"最高"中断运行花费时间在10个C指令运行时间内,保守估计在1us以内(通过逻辑分析仪也可看到)。这基本满足了大部分人需要,也应该能满足实时系统需求。
2:禁止中断,也是在特定IIC操控指令段运行的时候禁止中断,花费时间和方法1比应该少一点。

本文借鉴了 这篇文章的方法 《浅谈 STM32 硬件I2C的使用 (中断方式 无DMA无最高优先级)》,可baidu获得
但实际使用结果是,上文并没有完全解决硬件I2C的方法,上文在测试中没有出现异常时因为I2C被中断打断的次数不够密集,只有在外部中断刚好击中I2C运行的某两个指令之间的时候,I2C通信才会出现问题。

那么在解决I2C问题之前,需要创造一个足够强悍的中断,使得I2C中断在运行的时候,基本每执行一句话都被打断,这样才能有效验证I2C。以下测试环境都在STM32F103(72Mhz)芯片上运行

通过TIM2定时器产生中断,在中断函数里面进行延时,通过设定时间设定,产生一个基本都在中断函数里运行的状态,两个中断之间的时隙在1.25us,通过逻辑分析仪观察,在每个I2C的中断之间,例如开始发送中断(EV5)和收到地址响应中断(EV6)之间会被上述中断函数打断几十到上百次(由于被打断次数太多,没数。只是看逻辑分析仪上面密密麻麻的被不停打断)而EV5 和EV6中断之间运行的I2C代码一共也只有十几条,所以验证了I2C没执行一句会被打断一次。(如果还不放心,可以对一次发送或读取操作进行多次反复,确保I2C每条语句执行之间都会被打断)
中断函数为,特别注意 delay_usj(25);
  • void TIM2_IRQHandler(void)
  • {
  • if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
  • {
  • TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
  • LED_ON;
  • //nop;
  • delay_usj(25);
  • LED_OFF;
  • }
  • }

[color=rgb(51, 102, 153) !important]复制代码

延时函数
  • void delay_usj(u32 n)
  • {
  • u8 j;
  • while(n--)
  • for(j=0;j<7;j++);
  • }

[color=rgb(51, 102, 153) !important]复制代码

中断设置语句,特别注意TIM_TimeBaseStructure.TIM_Period = 22;//自动重装值  ,TIM_TimeBaseStructure.TIM_Prescaler = 71;     //36000

  • TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  • RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);
  • TIM_DeInit(TIM2);
  • TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  • TIM_TimeBaseStructure.TIM_Period = 22;//自动重装值
  • TIM_TimeBaseStructure.TIM_Prescaler = 71;//36000
  • TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1;
  • TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_Up;
  • TIM_TimeBaseStructure.TIM_RepetitionCounter =0x0;
  • TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
  • TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  • TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE);
  • TIM_Cmd(TIM2, ENABLE);

[color=rgb(51, 102, 153) !important]复制代码

通过特定时间设置,即上述特别注意的三个地方,通过这几个值,能保证在我的机器上stm32f103上产生连绵不断的中断,中断之间的运行时间有1.25us,在实际测试i2c时,在连续两个事件之间,例如ev5和ev6之间会被打断非常多次,而ev5和ev6之间的运行代码才寥寥十几行,再通过反复多次运行测试,可以确保了i2c的每条语句执行完毕后都会被外部中断打断。从而有效验证i2c是否编写正确。对于你的平台,可以通过观看中断中的LED_ON;LED_OFF(实际是某个GPIO电灯管教),通过检测逻辑分析仪来查看是否中断足够热烈,足够让I2C事件之间产生足够多的中断。这个延时设置需要稍微花一点时间才能找到。

说完测试环境,说如何编写I2C
根据相关芯片referencemanual和errata sheet,例如下文
I2C eventmanagement
Description
As describedin the I2C section of the STM8S microcontroller reference manual(RM0016),
theapplication firmware has to manage several software events beforethe current byte is
transferred.If the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are notmanaged
before thecurrent byte is transferred, problems may occur such as receivingan extra byte,
reading thesame data twice, or missing data.
Workaround
When the EV7,EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events cannot bemanaged
before thecurrent byte transfer, and before the acknowledge pulse when theACK control bit
changes, itis recommended to use I2C interrupts in nested mode and to makethem
uninterruptible byincreasing their priority to the highest priority in theapplication.
No fix isplanned for this limitation.
上述意思是说 EV7,EV7_1, EV6_1, EV6_3, EV2, EV8, andEV3这几个事件不能被打断,除了最高中端不会被打断外没有其他解决方法。

那么既然不能被打断,就是用文头说的两个解决发放解决。
对于1BYTE接收,用解决方法1
例子如下
  •             switch(RxLength)
  • {
  • case 1:
  • //I2C_StretchClockCmd(I2C1,ENABLE);//经过验证此句无用
  • I2C1->CR1 &=((uint16_t)0xFBFF);//I2C_AcknowledgeConfig(I2C1,DISABLE);
  • #ifdef ENABLEHIGHIRQ //暂时I2C1权限提升到NVIC_IRQChannelPreemptionPriority = 0,NVIC_IRQChannelSubPriority =15;实际上就是不可打断。是否用此保护块依赖于你的使用环境,如果没有外部中断或者外部中断优先级比i2c低,可以不使用此保护块
  • tmppriority = (0x700 - ((SCB->AIRCR) &(uint32_t)0x700))>> 0x08;
  • tmppre = (0x4 - tmppriority);
  • tmpsub = tmpsub >> tmppriority;
  • tmppriority = (uint32_t)0 << tmppre;//NVIC_IRQChannelPreemptionPriority = 0
  • tmppriority |= 15 & tmpsub; // NVIC_IRQChannelSubPriority =15;
  • tmppriority = tmppriority << 0x04;
  • NVIC->IP[I2C1_EV_IRQn] = tmppriority;//I2C1_EV_IRQn=31
  • NVIC->ISER[I2C1_EV_IRQn >> 0x05] =
  • (uint32_t)0x01 << (I2C1_EV_IRQn &(uint8_t)0x1F);
  • #endif
  • //以下两句为必须一起执行的语句,如果读了SR2后被外部中断打断,则I2C一但读取SR2后则硬件开始传送下一个数据,而STOP位没有被及时赋值,则导致I2C通信异常。加上保护块后则没有问题。
  • //在最高终端运行的时间是,上一句+本保护块2句+收尾几局,运行时间很短暂
  • (void) I2C1->SR2;//读SR2
  • I2C1->CR1 |= ((uint16_t)0x0200); //write stop bit;
  • #ifdef ENABLEHIGHIRQ //回复之前的I2C权限
  • tmppriority = (0x700 - ((SCB->AIRCR) &(uint32_t)0x700))>> 0x08;
  • tmppre = (0x4 - tmppriority);
  • tmpsub = tmpsub >> tmppriority;
  • tmppriority = (uint32_t)PreemptionPriority <<tmppre;
  • tmppriority |= SubPriority & tmpsub;
  • tmppriority = tmppriority << 0x04;
  • NVIC->IP[I2C1_EV_IRQn] = tmppriority;
  • NVIC->ISER[I2C1_EV_IRQn >> 0x05] =
  • (uint32_t)0x01 << (I2C1_EV_IRQn &(uint8_t)0x1F);
  • #endif

[color=rgb(51, 102, 153) !important]复制代码
2BYTE接收,用解决方法2:


  •              case2:
  • I2C1->CR1 |= ((uint16_t)0x0400);//I2C_AcknowledgeConfig(I2C1,ENABLE);
  • I2C1->CR1 |=I2C_NACKPosition_Next;//I2C_NACKPositionConfig(I2C1,I2C_NACKPosition_Next);
  • I2C_StretchClockCmd(I2C1,ENABLE);
  • I2C_ITConfig(I2C1, I2C_IT_EVT ,ENABLE);
  • level = rt_hw_interrupt_disable(); //保护块开始
  • (void) I2C1->SR2;// once addr is clearing .the above code isgoing to run ,and bus is busy.
  • I2C1->CR1 &=((uint16_t)0xFBFF);//I2C_AcknowledgeConfig(I2C1,DISABLE);
  • rt_hw_interrupt_enable(level);
  • return;

[color=rgb(51, 102, 153) !important]复制代码

rt_hw_interrupt_disable,rt_hw_interrupt_enable的意思是只允许NMI 和 hard  fault异常,其他中断/  异常都被屏蔽(当前 CPU优先级=0)。是我在rtthread rtos里面找到的代码


  • ;
  • rt_hw_interrupt_disable PROC
  • EXPORT rt_hw_interrupt_disable
  • MRS r0, PRIMASK
  • CPSID I
  • BX LR
  • ENDP
  • ;
  • rt_hw_interrupt_enable PROC
  • EXPORT rt_hw_interrupt_enable
  • MSR PRIMASK, r0
  • BX LR
  • ENDP

[color=rgb(51, 102, 153) !important]复制代码

代码用汇编编写,简洁快速。可以参看rtthread相关代码。

对于2byte以上,推荐dma方法,也请使用上述中断方法测试,可以确保dma的操控也不会出错。


实际使用效果如逻辑分析仪展示
S1是最低放大图片,由于中断过于频繁,导致I2C的每传送一个BYTE,或者说每一个中断事件之间都被密密麻麻的中断拥塞,导致I2C的运行时间被长时间阻隔,同时,I2C的中断运行代码也意味着每运行一句会被中断打断。
S2是最高放大图片,对应着一个EV5开始事件,CHANEL2的低电平意味着LED中断运行,高电平意味着LED中断退出,CPU运行其他程序和I2C中断。可以看到每个LED中断之间间隔很短(实际不是1.25us,受逻辑分析数据采样率影响,但也差不多,不影响分析)
S3可以看到在每个I2C事件之间都填充了非常多的LED中断事件,由于事件过多,以至于图片无法展示到下一个I2C中断
S4对应着某个I2C事件,可以看到I2C的事件处理完后和LED中断并行的画面
S1 可以看到SCL和SDA







]



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:103938 发表于 2016-1-20 21:10 | 只看该作者
这个好,楼主弄一个完整的例子更好。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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