<tfoot id="ygvxt"><menuitem id="ygvxt"></menuitem></tfoot>
    • <kbd id="ygvxt"></kbd>
      <th id="ygvxt"><progress id="ygvxt"></progress></th>

      您好,歡迎訪問上海意泓電子科技有限責任公司網(wǎng)站!
      4新聞資訊
      您的位置: 首頁 ->  新聞資訊 -> 單片機

      ?GPIO實現(xiàn)I2C從機的設(shè)計

      文章出處:單片機 責任編輯:上海意泓電子科技有限責任公司 發(fā)表時間:
      2018
      05-20

      在本階段的工作中,需要實現(xiàn)一個由GPIO模擬的I2C從機工程設(shè)計,以前只使用GPIO模擬I2C設(shè)計過主機,對于從機的設(shè)計,還是首次。下面就講本次工作中從機設(shè)計思想做詳細記錄。

      I2C的簡單總結(jié)

      對于I2C信號,需要有START,STOP,ACK,NACK,以及接收DATA。接收DATA是在SCL的低電平可能發(fā)生跳變,START和STOP是在高電平跳變。當SCL保持高電平的時候,SDA從H跳變到L,即為START;當SCL保持高電平的時候,SDA從L跳變到H,即為STOP。

      START信號: 
       
      圖1

      STOP信號 
       
      圖2

      ACK信號 
       
      圖3

      主機下發(fā)地址以及讀寫信號 

      圖4

      程序設(shè)計以及分析

      //為所使用的硬件平臺的寄存器配置

      #define WAIT_IIC_SCL_HIGH   while ( !GET_SCL_DAT )

      #define WAIT_IIC_SCL_LOW    while ( GET_SCL_DAT )

      #define WAIT_IIC_SDA_HIGH   while ( !GET_SDA_DAT )

      #define WAIT_IIC_SDA_LOW    while ( GET_SDA_DAT )


      #define IIC_WAIT_START      WAIT_IIC_SCL_HIGH;  WAIT_IIC_SDA_LOW    

      #define IIC_WAIT_STOP       WAIT_IIC_SCL_LOW; SDA_IN; WAIT_IIC_SCL_HIGH; WAIT_IIC_SDA_HIGH 


      #define IIC_SLAVE_SEND_LOW  WAIT_IIC_SCL_LOW; SDA_OUT; SET_SDA_LOW; WAIT_IIC_SCL_HIGH      

      #define IIC_SLAVE_SEND_HIGH WAIT_IIC_SCL_LOW; SDA_OUT; SET_SDA_HIGH; WAIT_IIC_SCL_HIGH


      #define IIC_SLAVE_SEND_ACK  IIC_SLAVE_SEND_LOW

      #define IIC_SLAVE_SEND_NAK  IIC_SLAVE_SEND_HIGH


      • 初始化

      void iic_init(void)  // 完成GPIO作為I2C的初始化

      完成GPIO時鐘寄存機配置等功能。

      • 接收地址以及讀寫命令模塊

      for(bitcount = 0; bitcount < 7; bitcount ++)

      {

          WAIT_IIC_SCL_LOW;                   

          WAIT_IIC_SCL_HIGH;

          iic_slv_addr <<= 1;  //先移位,再讀數(shù)

          if(GET_SDA_DAT)

              iic_slv_addr |= 0x01;

          else

              iic_slv_addr |= 0x00;

      }

      iic_slv_addr <<= 1;

      // 讀取7位地址


      WAIT_IIC_SCL_LOW;

      WAIT_IIC_SCL_HIGH;

      if(GET_SDA_DAT)

          iic_master_rw = 1;

      else

          iic_master_rw = 0;

      // 讀寫標志位


      if (iic_slv_addr == SLAVE_ADDR)

          IIC_SLAVE_SEND_ACK;    // 地址正確,從機發(fā)送ACK信號

      從機接收接口定義以及說明


      uint8_t L_i2c_rx( uint8_t xdata *pDestBuf, uint16_t *wRecLen);


      pDestBuf:接收數(shù)據(jù)保存的目的地址 wRecLen:實際接收到的數(shù)據(jù)的長度


      接收核心代碼分析



      while(!recFinish)

      {

          for(bitcount=0; bitcount<8; bitcount++)

          {

              while(GET_SCL_DAT);

              SDA_IN;

              while(!GET_SCL_DAT);


          r0 = GET_SDA_DAT;

          while(GET_SCL_DAT)

          {

              r1 = GET_SDA_DAT;

              if((r0 == 0) && (r1 == 1))

              {

                  recFinish = 1;

                  return 0;

              }

          }


          rxbyte <<= 1;

          if(r1)

              rxbyte |= 0x01;

          else

              rxbyte |= 0x00; 

      }

      buf[len] = rxbyte;

      len++;

      IIC_SLAVE_SEND_ACK;


      在從機接收完地址以后,如果讀寫標志位是寫(L),接下來,從機就會接收數(shù)據(jù),接收數(shù)據(jù)完成的標志為接收到了STOP信號,就必須在從機發(fā)完ACK后的第一個SCL高電平時檢測是否有SDA從H跳轉(zhuǎn)到L,如果發(fā)生了,接收程序結(jié)束,如果沒有發(fā)生跳變,繼續(xù)接收數(shù)據(jù)的bit7,bit6,……bit0.

       
      圖5

      根據(jù)圖5,在時鐘節(jié)拍①中從機發(fā)送ACK信號以后,因為GPIO的采樣頻率遠大于I2C的始終頻率(設(shè)計中使用100K),所以需要在發(fā)送ACK后,要等待SCL變?yōu)長,在上面的代碼中體現(xiàn)在c點,在時鐘節(jié)拍②中,SDA可能會發(fā)生變化。最重要的時需要在時鐘節(jié)拍③內(nèi)采樣SDA,判斷是否有變化(即從f到g中連續(xù)采樣SDA),如果發(fā)生了SDA從H到L,那么就認為接收到了STOP信號,跳出接收數(shù)據(jù)的函數(shù);如果發(fā)生SDA從L到H,會被認為是一個異常的信號(START信號,但不應該在此時出現(xiàn),上段代碼中沒有處理此異常情況,請注意),同樣也需要跳出接收數(shù)據(jù)的函數(shù);如果SDA沒有發(fā)生任何變化,同時等待到SCL發(fā)生由H到L的變化,則意味著接收到了下一字節(jié)的bit7……

      這樣最核心的問題就變?yōu)樵鯓优袛嘣跁r鐘節(jié)拍③中SDA是否發(fā)生了變化,并且發(fā)生了怎樣的變化。設(shè)計的思想為:在f點(上升沿)后取第一個SDA的采樣值r0,遍歷時鐘節(jié)拍③直到g點(下降沿),連續(xù)采樣SDA的下一個采樣值r1,當在時鐘節(jié)拍③內(nèi),只要發(fā)生了r1 != r0的時候,馬上跳出接收數(shù)據(jù)程序。但如果r1 === r0,在檢測到h點的時候,才把r1賦值給接收字節(jié)rxbyte.

      以上采樣能夠成功的一個前提是:SDA在SCL每一時鐘節(jié)拍的變化能夠被采樣到。

      在此假設(shè)GPIO的時鐘為2MHz,SCL的傳輸速度為100KHz,時序關(guān)系如下圖所示:


      圖6

      上圖是比較理想的SCL的時鐘周期信號,在每半個SCL的時鐘周期中,有10個采樣點,這樣確保了SCL上升沿后的第一個GPIO采樣到了r0=0,也能在后5個采樣點中采到了r1=1,在這種情況下,不會發(fā)生任何錯誤。

      但是,實際情況并非如此,在GPIO模擬I2C的SCL信號中,占空比并不是50%,如果此時SDA的變化在GPIO第一個采樣沿之前就發(fā)生了變化,那么就無法采樣到正確的電平變化信號。

      所以,以上設(shè)計方法能夠成功的前提為:SDA并不會在緊靠著SCL的上升沿或者下降沿而變化,也就是說SDA的任何變化,都能被GPIO的時鐘采樣到。

      • 從機發(fā)送接口定義以及說明

      uint8_t L_i2c_tx(const uint8_t xdata * pSendBuf, uint16_t wSendLen);


      pSendBuf:發(fā)送數(shù)據(jù)緩存地址 wSendLen:發(fā)送數(shù)據(jù)的長度


      發(fā)送核心代碼分析

      for(bytecount = 0; bytecount < len; bytecount ++)

      {        

          txmask = 0x80;

          txbyte = buf[bytecount];

          for(bitcount = 0; bitcount < 8; bitcount ++)

          {

              WAIT_IIC_SCL_LOW;

              SDA_OUT;

              if ( txbyte & txmask )

                  SET_SDA_HIGH;   

              else    

                  SET_SDA_LOW;  

              WAIT_IIC_SCL_HIGH;

              txmask = txmask >> 1;       

          }


          WAIT_IIC_SCL_LOW;                                               

          SDA_IN;

          WAIT_IIC_SCL_HIGH;

          if ( GET_SDA_DAT )  

              break;

      }

      return (bytecount);


      for(bytecount = 0; bytecount < len; bytecount ++)

      {        

          txmask = 0x80;

          txbyte = buf[bytecount];

          for(bitcount = 0; bitcount < 8; bitcount ++)

          {

              WAIT_IIC_SCL_LOW;

              SDA_OUT;

              if ( txbyte & txmask )

                  SET_SDA_HIGH;   

              else    

                  SET_SDA_LOW;  

              WAIT_IIC_SCL_HIGH;

              txmask = txmask >> 1;       

          }


          WAIT_IIC_SCL_LOW;                                               

          SDA_IN;

          WAIT_IIC_SCL_HIGH;

          if ( GET_SDA_DAT )  

              break;

      }

      return (bytecount);

       
      流程圖

      上海意泓電子科技有限責任公司 版權(quán)所有 未經(jīng)授權(quán)禁止復制或鏡像

      CopyRight 2020-2025 www.hljhgw.com All rights reserved   滬ICP備2021005866號

      国产强伦姧在线观看,…中文天堂在线一区,亚洲欧洲精品污网站在线观看,在线视频综合站
      <tfoot id="ygvxt"><menuitem id="ygvxt"></menuitem></tfoot>
        • <kbd id="ygvxt"></kbd>
          <th id="ygvxt"><progress id="ygvxt"></progress></th>