找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 4061|回復(fù): 1
打印 上一主題 下一主題
收起左側(cè)

STM32 IIC 硬件通信解決關(guān)鍵點(diǎn),已驗(yàn)證通過(guò)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:102668 發(fā)表于 2016-1-11 04:54 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本帖最后由 51hei社區(qū) 于 2016-1-11 04:55 編輯


本文已假設(shè)你了解了STM32的IIC的基礎(chǔ)知識(shí),也大概了解了IIC會(huì)出現(xiàn)問(wèn)題。
本文只簡(jiǎn)單介紹兩個(gè)具體例子,其他依照處理即可。
準(zhǔn)備物品:一個(gè)邏輯分析儀,這樣才能可靠了解I2C在哪里出錯(cuò)了。某寶上很便宜,100以內(nèi)。
例子:1BYTE接受和2BYTE接收。
解決辦法有兩種,意思都一樣:
1:暫時(shí)提升權(quán)限到“最高”,在"最高"中斷運(yùn)行花費(fèi)時(shí)間在10個(gè)C指令運(yùn)行時(shí)間內(nèi),保守估計(jì)在1us以內(nèi)(通過(guò)邏輯分析儀也可看到)。這基本滿足了大部分人需要,也應(yīng)該能滿足實(shí)時(shí)系統(tǒng)需求。
2:禁止中斷,也是在特定IIC操控指令段運(yùn)行的時(shí)候禁止中斷,花費(fèi)時(shí)間和方法1比應(yīng)該少一點(diǎn)。

本文借鑒了 這篇文章的方法 《淺談 STM32 硬件I2C的使用 (中斷方式 無(wú)DMA無(wú)最高優(yōu)先級(jí))》,可baidu獲得
但實(shí)際使用結(jié)果是,上文并沒(méi)有完全解決硬件I2C的方法,上文在測(cè)試中沒(méi)有出現(xiàn)異常時(shí)因?yàn)镮2C被中斷打斷的次數(shù)不夠密集,只有在外部中斷剛好擊中I2C運(yùn)行的某兩個(gè)指令之間的時(shí)候,I2C通信才會(huì)出現(xiàn)問(wèn)題。

那么在解決I2C問(wèn)題之前,需要?jiǎng)?chuàng)造一個(gè)足夠強(qiáng)悍的中斷,使得I2C中斷在運(yùn)行的時(shí)候,基本每執(zhí)行一句話都被打斷,這樣才能有效驗(yàn)證I2C。以下測(cè)試環(huán)境都在STM32F103(72Mhz)芯片上運(yùn)行

通過(guò)TIM2定時(shí)器產(chǎn)生中斷,在中斷函數(shù)里面進(jìn)行延時(shí),通過(guò)設(shè)定時(shí)間設(shè)定,產(chǎn)生一個(gè)基本都在中斷函數(shù)里運(yùn)行的狀態(tài),兩個(gè)中斷之間的時(shí)隙在1.25us,通過(guò)邏輯分析儀觀察,在每個(gè)I2C的中斷之間,例如開始發(fā)送中斷(EV5)和收到地址響應(yīng)中斷(EV6)之間會(huì)被上述中斷函數(shù)打斷幾十到上百次(由于被打斷次數(shù)太多,沒(méi)數(shù)。只是看邏輯分析儀上面密密麻麻的被不停打斷)而EV5 和EV6中斷之間運(yùn)行的I2C代碼一共也只有十幾條,所以驗(yàn)證了I2C沒(méi)執(zhí)行一句會(huì)被打斷一次。(如果還不放心,可以對(duì)一次發(fā)送或讀取操作進(jìn)行多次反復(fù),確保I2C每條語(yǔ)句執(zhí)行之間都會(huì)被打斷)
中斷函數(shù)為,特別注意 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]復(fù)制代碼

延時(shí)函數(shù)
  • void delay_usj(u32 n)
  • {
  • u8 j;
  • while(n--)
  • for(j=0;j<7;j++);
  • }

[color=rgb(51, 102, 153) !important]復(fù)制代碼

中斷設(shè)置語(yǔ)句,特別注意TIM_TimeBaseStructure.TIM_Period = 22;//自動(dòng)重裝值  ,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;//自動(dòng)重裝值
  • 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]復(fù)制代碼

通過(guò)特定時(shí)間設(shè)置,即上述特別注意的三個(gè)地方,通過(guò)這幾個(gè)值,能保證在我的機(jī)器上stm32f103上產(chǎn)生連綿不斷的中斷,中斷之間的運(yùn)行時(shí)間有1.25us,在實(shí)際測(cè)試i2c時(shí),在連續(xù)兩個(gè)事件之間,例如ev5和ev6之間會(huì)被打斷非常多次,而ev5和ev6之間的運(yùn)行代碼才寥寥十幾行,再通過(guò)反復(fù)多次運(yùn)行測(cè)試,可以確保了i2c的每條語(yǔ)句執(zhí)行完畢后都會(huì)被外部中斷打斷。從而有效驗(yàn)證i2c是否編寫正確。對(duì)于你的平臺(tái),可以通過(guò)觀看中斷中的LED_ON;LED_OFF(實(shí)際是某個(gè)GPIO電燈管教),通過(guò)檢測(cè)邏輯分析儀來(lái)查看是否中斷足夠熱烈,足夠讓I2C事件之間產(chǎn)生足夠多的中斷。這個(gè)延時(shí)設(shè)置需要稍微花一點(diǎn)時(shí)間才能找到。

說(shuō)完測(cè)試環(huán)境,說(shuō)如何編寫I2C
根據(jù)相關(guān)芯片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.
上述意思是說(shuō) EV7,EV7_1, EV6_1, EV6_3, EV2, EV8, andEV3這幾個(gè)事件不能被打斷,除了最高中端不會(huì)被打斷外沒(méi)有其他解決方法。

那么既然不能被打斷,就是用文頭說(shuō)的兩個(gè)解決發(fā)放解決。
對(duì)于1BYTE接收,用解決方法1
例子如下
  •             switch(RxLength)
  • {
  • case 1:
  • //I2C_StretchClockCmd(I2C1,ENABLE);//經(jīng)過(guò)驗(yàn)證此句無(wú)用
  • I2C1->CR1 &=((uint16_t)0xFBFF);//I2C_AcknowledgeConfig(I2C1,DISABLE);
  • #ifdef ENABLEHIGHIRQ //暫時(shí)I2C1權(quán)限提升到NVIC_IRQChannelPreemptionPriority = 0,NVIC_IRQChannelSubPriority =15;實(shí)際上就是不可打斷。是否用此保護(hù)塊依賴于你的使用環(huán)境,如果沒(méi)有外部中斷或者外部中斷優(yōu)先級(jí)比i2c低,可以不使用此保護(hù)塊
  • 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
  • //以下兩句為必須一起執(zhí)行的語(yǔ)句,如果讀了SR2后被外部中斷打斷,則I2C一但讀取SR2后則硬件開始傳送下一個(gè)數(shù)據(jù),而STOP位沒(méi)有被及時(shí)賦值,則導(dǎo)致I2C通信異常。加上保護(hù)塊后則沒(méi)有問(wèn)題。
  • //在最高終端運(yùn)行的時(shí)間是,上一句+本保護(hù)塊2句+收尾幾局,運(yùn)行時(shí)間很短暫
  • (void) I2C1->SR2;//讀SR2
  • I2C1->CR1 |= ((uint16_t)0x0200); //write stop bit;
  • #ifdef ENABLEHIGHIRQ //回復(fù)之前的I2C權(quán)限
  • 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]復(fù)制代碼
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(); //保護(hù)塊開始
  • (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]復(fù)制代碼

rt_hw_interrupt_disable,rt_hw_interrupt_enable的意思是只允許NMI 和 hard  fault異常,其他中斷/  異常都被屏蔽(當(dāng)前 CPU優(yōu)先級(jí)=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]復(fù)制代碼

代碼用匯編編寫,簡(jiǎn)潔快速?梢詤⒖磖tthread相關(guān)代碼。

對(duì)于2byte以上,推薦dma方法,也請(qǐng)使用上述中斷方法測(cè)試,可以確保dma的操控也不會(huì)出錯(cuò)。


實(shí)際使用效果如邏輯分析儀展示
S1是最低放大圖片,由于中斷過(guò)于頻繁,導(dǎo)致I2C的每傳送一個(gè)BYTE,或者說(shuō)每一個(gè)中斷事件之間都被密密麻麻的中斷擁塞,導(dǎo)致I2C的運(yùn)行時(shí)間被長(zhǎng)時(shí)間阻隔,同時(shí),I2C的中斷運(yùn)行代碼也意味著每運(yùn)行一句會(huì)被中斷打斷。
S2是最高放大圖片,對(duì)應(yīng)著一個(gè)EV5開始事件,CHANEL2的低電平意味著LED中斷運(yùn)行,高電平意味著LED中斷退出,CPU運(yùn)行其他程序和I2C中斷。可以看到每個(gè)LED中斷之間間隔很短(實(shí)際不是1.25us,受邏輯分析數(shù)據(jù)采樣率影響,但也差不多,不影響分析)
S3可以看到在每個(gè)I2C事件之間都填充了非常多的LED中斷事件,由于事件過(guò)多,以至于圖片無(wú)法展示到下一個(gè)I2C中斷
S4對(duì)應(yīng)著某個(gè)I2C事件,可以看到I2C的事件處理完后和LED中斷并行的畫面
S1 可以看到SCL和SDA







]



分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:103938 發(fā)表于 2016-1-20 21:10 | 只看該作者
這個(gè)好,樓主弄一個(gè)完整的例子更好。
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表