新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 關(guān)于MSP硬件I2C講解

關(guān)于MSP硬件I2C講解

作者: 時(shí)間:2016-11-21 來源:網(wǎng)絡(luò) 收藏

0.前言

對(duì)于大多數(shù)單片機(jī)來說,I2C成了一個(gè)老大難問題。從51時(shí)代開始,軟件模擬I2C成了主流,甚至到ARMCortex M3大行其道的今天,軟件模擬I2C依然是使用最廣的方法。雖然軟件模擬可以解決所有的問題,但是總感覺沒有充分發(fā)揮MCU內(nèi)部的硬件資源。查閱了所有關(guān)于MSP430F5系列的圖書,沒有關(guān)于硬件I2C的應(yīng)用代碼,自己通過調(diào)試摸索,把經(jīng)驗(yàn)總結(jié)之后和大家分享,希望大家喜歡。同時(shí),I2C的使用可以分為等待法和中斷法,從理解的角度來說等待法思路清晰易于上手,從功耗的角度出發(fā),中斷法可以靈活的進(jìn)入低功耗模式,但是不易理解。本文先從等待法入手。

本文引用地址:http://m.butianyuan.cn/article/201611/319192.htm

MSP430F5系列的硬件I2C使用大致會(huì)有以下問題:

【I2C地址設(shè)定】一般情況下I2C的7位地址被寫成了8位長(zhǎng)度,最低位無效。例如AT24C02的I2C地址為0xA0,其實(shí)真正的7位地址為0x50。而MSP430正是需要填入這7位地址0x50。

【I2C停止位發(fā)送】在I2C讀操作過程中,讀取最后一個(gè)字節(jié)之后MCU應(yīng)向從機(jī)發(fā)送無應(yīng)答,MSP430F5系列的MCU發(fā)送無應(yīng)答的操作將自動(dòng)完成,這就以為在讀取最后一個(gè)字節(jié)內(nèi)容時(shí),應(yīng)先操作停止位相關(guān)寄存器。

【I2C起始位發(fā)送】如果仔細(xì)分析MSP430F5參考手冊(cè),將會(huì)發(fā)現(xiàn)讀操作和寫操作發(fā)送I2C起始位時(shí)略有不同。寫操作時(shí)需要先向TXBUF中寫入數(shù)據(jù),之后才可以等待TXSTT標(biāo)志位變?yōu)?,而讀操作和寫操作稍有不同。

【AT24C02操作時(shí)序圖】

1.初始化設(shè)置

1.1代碼實(shí)現(xiàn)

  1. voiducb0_config(void)
  2. {
  3. P3SEL&=~BIT2;//P3.2@UCB0SCL
  4. P3DIR|=BIT2;
  5. P3OUT|=BIT2;
  6. //輸出9個(gè)時(shí)鐘以恢復(fù)I2C總線狀態(tài)
  7. for(uint8_ti=0;i<9;i++)
  8. {
  9. P3OUT|=BIT2;
  10. __delay_cycles(8000);
  11. P3OUT&=~BIT2;
  12. __delay_cycles(8000);
  13. }
  14. P3SEL|=(BIT1+BIT2);//P3.1@UCB0SDAP3.2@UCB0SCL
  15. //P3.1@ISP.1P3.2@ISP.5
  16. UCB0CTL1|=UCSWRST;
  17. UCB0CTL0=UCMST+UCMODE_3+UCSYNC;//I2C主機(jī)模式
  18. UCB0CTL1|=UCSSEL_2;//選擇SMCLK
  19. UCB0BR0=40;
  20. UCB0BR1=0;
  21. UCB0CTL0&=~UCSLA10;//7位地址模式
  22. UCB0I2CSA=EEPROM_ADDRESS;//EEPROM地址
  23. UCB0CTL1&=~UCSWRST;
  24. }

1.2代碼分析

I2C從設(shè)備的地址一般有以下通俗說法——7位地址,寫地址(寫控制字)和讀地址(讀控制字)。1個(gè)I2C通信的控制字節(jié)(I2C啟動(dòng)之后傳送的第一個(gè)字節(jié))由7位I2C地址和1位讀寫標(biāo)志位組成,7位I2C地址即7位地址,若讀寫標(biāo)志位為讀標(biāo)志(讀寫標(biāo)志位置位)加上7位I2C地址便組成了讀地址(讀控制字),若讀寫標(biāo)志位為寫標(biāo)志(讀寫標(biāo)志位清零)加上7位地址便組成了寫地址(寫控制字)。例如AT24C02的I2C7位地址為0x50,讀地址(讀控制字)為0xA1,寫地址(寫控制字)為0xA1。

在MSP430F5系列中,I2CSA地址寄存器應(yīng)寫入7位地址,參照上面的例子應(yīng)寫入0X50。至于I2C讀寫位的控制由CTL1寄存器完成,用戶無需干預(yù)。

在I2C設(shè)置開始之前,可以先通過SCL端口發(fā)送9個(gè)時(shí)鐘信號(hào),該時(shí)鐘信號(hào)可以是I2C從機(jī)芯片從一種錯(cuò)誤的通信狀態(tài)恢復(fù),雖然這9個(gè)時(shí)鐘信號(hào)不起眼但是對(duì)于調(diào)試過程來說非常有用。例如在調(diào)試過程中,錯(cuò)誤的發(fā)送了停止位,若再次啟動(dòng)調(diào)試則I2C從設(shè)備仍處于一種錯(cuò)誤的狀態(tài),這9個(gè)時(shí)鐘信號(hào)可以把I2C從設(shè)備從錯(cuò)誤的狀態(tài)“拉”回來。

2.寫單個(gè)字節(jié)

向I2C從設(shè)備寫入單個(gè)字節(jié)應(yīng)該是最為簡(jiǎn)單的一個(gè)操作,因?yàn)樗械目刂茩?quán)都在主機(jī)手中。寫單個(gè)字節(jié)實(shí)際包括了2個(gè)重要部分,一個(gè)便是寫寄存器地址,另一個(gè)便是寫寄存器內(nèi)容。對(duì)于AT24C02而言,存儲(chǔ)內(nèi)容的字節(jié)長(zhǎng)度為一個(gè)字節(jié),而對(duì)于容量更大的EEPROM而言,存儲(chǔ)地址可為兩個(gè)字節(jié)。

2.1 代碼實(shí)現(xiàn)

  1. uint8_teeprom_writebyte(uint8_tword_addr,uint8_tword_value)
  2. {
  3. while(UCB0CTL1&UCTXSTP);
  4. UCB0CTL1|=UCTR;//寫模式
  5. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位
  6. UCB0TXBUF=word_addr;//發(fā)送字節(jié)地址
  7. //等待UCTXIFG=1與UCTXSTT=0同時(shí)變化等待一個(gè)標(biāo)志位即可
  8. while(!(UCB0IFG&UCTXIFG))
  9. {
  10. if(UCB0IFG&UCNACKIFG)//若無應(yīng)答UCNACKIFG=1
  11. {
  12. return1;
  13. }
  14. }
  15. UCB0TXBUF=word_value;//發(fā)送字節(jié)內(nèi)容
  16. while(!(UCB0IFG&UCTXIFG));//等待UCTXIFG=1
  17. UCB0CTL1|=UCTXSTP;
  18. while(UCB0CTL1&UCTXSTP);//等待發(fā)送完成
  19. return0;
  20. }

2.2 代碼分析

關(guān)于代碼出口的說明,關(guān)于I2C的讀寫函數(shù),若返回值為0說明所有的操作正常,若返回值為非0說明操作有誤,例如1代表從機(jī)無應(yīng)答。這種組合方式可能與各位的編程習(xí)慣有出入,一般認(rèn)為返回1表示操作成功,而返回0表示操作失敗。這種方式的問題便是無法有效的表達(dá)錯(cuò)誤原因,因?yàn)?ldquo;0”只有一個(gè),而非“0”卻有很多。

寫單個(gè)字節(jié)可以劃分為——從機(jī)寫地址發(fā)送、寄存器地址發(fā)送、寄存器內(nèi)容發(fā)送。寄存器地址的發(fā)送由MSP430自動(dòng)完成,這和軟件模擬的操作有所區(qū)別。請(qǐng)勿發(fā)送I2C從機(jī)地址,若操作AT24C02發(fā)送需要寫入的存儲(chǔ)字節(jié)的首地址即可。

在單字節(jié)和多字節(jié)寫操作過程中,尤其要注意UCTXSTT標(biāo)志位的變化位置。UCTXSTT標(biāo)志位會(huì)在從機(jī)接收完寫控制字節(jié)或讀控制字節(jié)之后變化,但是在寫控制字節(jié)發(fā)送之后,必須先填充TXBUF,再嘗試等待STT標(biāo)志位復(fù)位,此時(shí)STT標(biāo)志位和TXIFG標(biāo)志位會(huì)同時(shí)變化。若從機(jī)沒有應(yīng)答,那么NACK標(biāo)志位也會(huì)發(fā)生變化。再次強(qiáng)調(diào)需要先填充TXBUF,在等待STT標(biāo)志位復(fù)位。以下代碼將導(dǎo)致程序一直停留在while(UCB0IFG & UCTXSTT)處,具體的原因可查看MSP430參考手冊(cè)。

  1. while(UCB0CTL1&UCTXSTP);
  2. UCB0CTL1|=UCTR;//寫模式
  3. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位
  4. //等待UCTXSTT=0同時(shí)變化,但是很遺憾該變化不會(huì)發(fā)送
  5. while(UCB0IFG&UCTXSTT);
  6. UCB0TXBUF=word_addr;//發(fā)送字節(jié)地址

3.寫多個(gè)字節(jié)

3.1代碼實(shí)現(xiàn)

  1. uint8_teeprom_writepage(uint8_tword_addr,uint8_t*pword_buf,uint8_tlen)
  2. {
  3. while(UCB0CTL1&UCTXSTP);
  4. UCB0CTL1|=UCTR;//寫模式
  5. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位
  6. UCB0TXBUF=word_addr;//發(fā)送字節(jié)地址
  7. //等待UCTXIFG=1與UCTXSTT=0同時(shí)變化等待一個(gè)標(biāo)志位即可
  8. while(!(UCB0IFG&UCTXIFG))
  9. {
  10. if(UCB0IFG&UCNACKIFG)//若無應(yīng)答UCNACKIFG=1
  11. {
  12. return1;
  13. }
  14. }
  15. for(uint8_ti=0;i
  16. {
  17. UCB0TXBUF=*pword_buf++;//發(fā)送寄存器內(nèi)容
  18. while(!(UCB0IFG&UCTXIFG));//等待UCTXIFG=1
  19. }
  20. UCB0CTL1|=UCTXSTP;
  21. while(UCB0CTL1&UCTXSTP);//等待發(fā)送完成
  22. return0;
  23. }

3.2 代碼分析

多字節(jié)寫函數(shù)和單字節(jié)寫函數(shù)相似,不做過多的解釋。

4.讀單個(gè)字節(jié)

單字節(jié)讀函數(shù)是4中讀寫函數(shù)中最為復(fù)雜的,復(fù)雜的原因在于讀最后一個(gè)字節(jié)之前就需要操作UCTXSTP標(biāo)志位。

4.1 代碼實(shí)現(xiàn)

  1. uint8_teeprom_readbyte(uint8_tword_addr,uint8_t*pword_value)
  2. {
  3. UCB0CTL1|=UCTR;//寫模式
  4. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位和寫控制字節(jié)
  5. UCB0TXBUF=word_addr;//發(fā)送字節(jié)地址,必須要先填充TXBUF
  6. //等待UCTXIFG=1與UCTXSTT=0同時(shí)變化等待一個(gè)標(biāo)志位即可
  7. while(!(UCB0IFG&UCTXIFG))
  8. {
  9. if(UCB0IFG&UCNACKIFG)//若無應(yīng)答UCNACKIFG=1
  10. {
  11. return1;
  12. }
  13. }
  14. UCB0CTL1&=~UCTR;//讀模式
  15. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位和讀控制字節(jié)
  16. while(UCB0CTL1&UCTXSTT);//等待UCTXSTT=0
  17. //若無應(yīng)答UCNACKIFG=1
  18. UCB0CTL1|=UCTXSTP;//先發(fā)送停止位
  19. while(!(UCB0IFG&UCRXIFG));//讀取字節(jié)內(nèi)容
  20. *pword_value=UCB0RXBUF;//讀取BUF寄存器在發(fā)送停止位之后
  21. while(UCB0CTL1&UCTXSTP);
  22. return0;
  23. }

4.2代碼分析

這段代碼給人一個(gè)錯(cuò)覺,MSP430先發(fā)送了停止位,然后再讀取了一個(gè)字節(jié)內(nèi)容。其實(shí)實(shí)際情況并不是這樣的。I2C讀操作時(shí),主機(jī)讀取最后一個(gè)字節(jié)內(nèi)容之后,應(yīng)向從機(jī)發(fā)送無應(yīng)答NACK(無應(yīng)答區(qū)別于應(yīng)答),之后主機(jī)發(fā)送停止位。MSP430為了完成這一組合動(dòng)作,要求用戶提前操作UCTXSTP標(biāo)志位,在讀取RXBUF之后做出發(fā)送NACK和I2C停止位的“組合動(dòng)作”。

  1. while(!(UCB0IFG&UCRXIFG));
  2. *pword_value=UCB0RXBUF;//讀取BUF寄存器在發(fā)送停止位之后
  3. UCB0CTL1|=UCTXSTP;//發(fā)送停止位

以上代碼可能導(dǎo)致后續(xù)的I2C操作無法進(jìn)行。

5.讀多個(gè)字節(jié)

5.1代碼實(shí)現(xiàn)

  1. uint8_teeprom_readpage(uint8_tword_addr,uint8_t*pword_buf,uint8_tlen)
  2. {
  3. while(UCB0CTL1&UCTXSTP);
  4. UCB0CTL1|=UCTR;//寫模式
  5. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位和寫控制字節(jié)
  6. UCB0TXBUF=word_addr;//發(fā)送字節(jié)地址
  7. //等待UCTXIFG=1與UCTXSTT=0同時(shí)變化等待一個(gè)標(biāo)志位即可
  8. while(!(UCB0IFG&UCTXIFG))
  9. {
  10. if(UCB0IFG&UCNACKIFG)//若無應(yīng)答UCNACKIFG=1
  11. {
  12. return1;
  13. }
  14. }
  15. UCB0CTL1&=~UCTR;//讀模式
  16. UCB0CTL1|=UCTXSTT;//發(fā)送啟動(dòng)位和讀控制字節(jié)
  17. while(UCB0CTL1&UCTXSTT);//等待UCTXSTT=0
  18. //若無應(yīng)答UCNACKIFG=1
  19. for(uint8_ti=0;i
  20. {
  21. while(!(UCB0IFG&UCRXIFG));//讀取字節(jié)內(nèi)容,不包括最后一個(gè)字節(jié)內(nèi)容
  22. *pword_buf++=UCB0RXBUF;
  23. }
  24. UCB0CTL1|=UCTXSTP;//在接收最后一個(gè)字節(jié)之前發(fā)送停止位
  25. while(!(UCB0IFG&UCRXIFG));//讀取最后一個(gè)字節(jié)內(nèi)容
  26. *pword_buf=UCB0RXBUF;
  27. while(UCB0CTL1&UCTXSTP);
  28. return0;
  29. }

5.2代碼分析

讀單個(gè)字節(jié)和寫單個(gè)字節(jié)相似。唯一需要注意的是,寫操作需要先填充TXBUF,而讀操作不存在這個(gè)問題。試想一下,I2C寫操作時(shí)必定會(huì)向I2C從機(jī)寫入一個(gè)字節(jié)內(nèi)容,所以先填充TXBUF也是合情合理的事情,填充TXBUF之后MSP430會(huì)進(jìn)行一連串的動(dòng)作——發(fā)送I2C起始位、I2C讀控制器和寫入從機(jī)的第一個(gè)字節(jié)。

6 單元測(cè)試

單元測(cè)試分為兩個(gè)部分。單字節(jié)寫函數(shù)和單字節(jié)讀函數(shù)分為一組,先使用單字節(jié)邪惡函數(shù)向某地址寫入某內(nèi)容,在使用單字節(jié)讀函數(shù)讀出某內(nèi)容,如果寫入的參數(shù)和讀出的內(nèi)容相同,則測(cè)試通過。多字節(jié)寫函數(shù)和多字節(jié)度函數(shù)分為一組,測(cè)試過程相似,不同的是寫入的內(nèi)容從一個(gè)變?yōu)榱诉B續(xù)8個(gè)。請(qǐng)注意AT24C02的頁大小為8,若從頁首地址開始,最大的寫字節(jié)個(gè)數(shù)為8。

另外,EEPROM寫操作之后需要有10ms的延時(shí),否則將無法進(jìn)行寫操作和讀操作。具體請(qǐng)查看AT24C02數(shù)據(jù)手冊(cè)。

6.1 測(cè)試代碼

  1. voideeprom_config()
  2. {
  3. #ifDEBUF_EEPROM_I2C
  4. uint8_ttest_byte1=0x0B;
  5. uint8_ttest_byte2=0x01;
  6. /*
  7. step1向地址0x00寫入某個(gè)值,例如0x0B
  8. 然后讀出地址0x00結(jié)果,判斷該值是否為0x0B
  9. */
  10. eeprom_writebyte(0x00,test_byte1);
  11. delay_ms(10);
  12. eeprom_readbyte(0x00,&test_byte2);
  13. assert_param(test_byte1==test_byte2);
  14. if(test_byte1==test_byte2)
  15. {
  16. printf("ByteReadandByteWriteTestPassrn");
  17. }
  18. /*
  19. step2以地址0x08作為起始地址,連續(xù)寫入8個(gè)字節(jié)數(shù)據(jù)
  20. 再連續(xù)從該起始地址讀取8字節(jié)內(nèi)容,比較寫入和讀出字節(jié)內(nèi)容
  21. 成功的條件為寫入和讀取字節(jié)內(nèi)容相同
  22. */
  23. uint8_ttest_buf1[8]={1,2,3,4,5,6,7,8};
  24. uint8_ttest_buf2[8]={0,0,0,0,0,0,0,0};
  25. eeprom_writepage(0x08,test_buf1,8);
  26. delay_ms(10);
  27. eeprom_readpage(0x08,test_buf2,8);
  28. assert_param(memcmp(test_buf1,test_buf2,8)==0);
  29. if(!memcmp(test_buf1,test_buf2,8))
  30. {
  31. printf("PageReadandPageWriteTestPass!rn");
  32. }
  33. #endif
  34. }

6.2 測(cè)試結(jié)果



關(guān)鍵詞: MSP硬件I2

評(píng)論


技術(shù)專區(qū)

關(guān)閉