久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 5665|回復: 1
打印 上一主題 下一主題
收起左側

[原創]STM32硬件I2C和軟件模擬I2C實現對比

[復制鏈接]
跳轉到指定樓層
樓主
一、  概述
    I2C是一種占用管腳少、傳送可靠和傳輸速率較好的低速總線,在嵌入式設備中被廣泛使用到。I2C 接口接收和發送數據,并將數據從串行轉換成并,或并轉換成串行。可以開啟或禁止中斷。接口通過數據引腳(SDA)和時鐘引腳(SCL)連接到I2C總線。允許連接到標準(高至100 kHz)或快速(高至400 kHz) I2C總線。
    STM32一般有2-3組硬件I2C,以STM32F103CB為例,其硬件I2C情況如下——
  
I2C編號
  
I2C名稱
對應管腳
1
I2C1
PB6, PB7
2
I2C2
PB10, PB11
所謂硬件I2C,即STM32芯片設計了SDA/SCL兩個管腳內部已符合觸發I2C的時序、協議、仲裁和定時等功能塊,軟件開發人員只需調用簡單的API即可方便使用I2C功能了。
    此外,由于硬件設計的原因,需要使用很多個I2C或不巧硬件I2C管腳被占用,此時就需要通過GPIO軟件模擬I2C,它是通過軟件來實現I2C的時序、協議、仲裁和定時等完整的機制。
    原理和概念部分不再贅述,大家很容易在網上找到各類相關資料進行了解和學習,今天我們詳細陳述具體實現過程和比較兩者的一些差異,主要以代碼作為表述形式。
二、   硬件I2C的實現
       硬件I2C調用的api接口均在stm32f10x_i2c.c文件中,通過這些API,我們需要實現
(1)  I2C的初始化;
(2)  I2C讀和寫的接口函數,供外部調用。
初始化代碼如下——
  
#define  CPS1848_I2C                    I2C2
  
  
#define  CHECK_BUSY(checkflag,busyendflag,dwCnt, dwTimeOut) \
  
    for (dwCnt =0; dwCnt< dwTimeOut;  dwCnt++)  \
  
    {   \
  
        if((checkflag) == (busyendflag))\
  
        {\
  
            break;\
  
        }\
  
    }
  
  
#define  CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut)    \
  
    if (dwBusyCount >= dwTimeOut)   \
  
    {    \
  
        return ERR_WAIT_BUSY;   \
  
}
  
  
  
//  I2C 外設(CPS1848)初始化
  
void  I2C_CPS1848_Init(BYTE ucBoardAddr)
  
{
  
    //GPIO初始化
  
    GPIO_InitTypeDef  GPIO_InitStructure;
  
    I2C_InitTypeDef  I2C_InitStructure;
  
  
    /* 使能與 I2Cx 引腳的GPIO的時鐘 */
  
     RCC_APB2PeriphClockCmd(CPS1848_I2C_SCL_GPIO_CLK |  CPS1848_I2C_SDA_GPIO_CLK, ENABLE);
  
    /* 使能 I2Cx 外設時鐘 */
  
    RCC_APB1PeriphClockCmd(CPS1848_I2C_CLK,  ENABLE);
  
  
    /* Reset I2Cx IP */
  
    RCC_APB1PeriphResetCmd(CPS1848_I2C_CLK,  ENABLE);
  
  
    /* Release reset signal of I2Cx IP */
  
    RCC_APB1PeriphResetCmd(CPS1848_I2C_CLK,  DISABLE);
  
  
    /* I2Cx_SCL */
  
    GPIO_InitStructure.GPIO_Pin   = CPS1848_I2C_SCL_PIN;
  
    GPIO_InitStructure.GPIO_Speed =  GPIO_Speed_50MHz;
  
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
  
    GPIO_Init(CPS1848_I2C_SCL_PORT,  &GPIO_InitStructure);
  
  
    /* I2Cx_SDA*/
  
    GPIO_InitStructure.GPIO_Pin   = CPS1848_I2C_SDA_PIN;
  
    GPIO_InitStructure.GPIO_Speed =  GPIO_Speed_50MHz;
  
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
  
    GPIO_Init(CPS1848_I2C_SDA_PORT,  &GPIO_InitStructure);
  
  
    //模式初始化
  
    /* I2Cx 配置 */
  
    I2C_InitStructure.I2C_Mode =  I2C_Mode_SMBusHost/*I2C_Mode_I2C */;
  
    /* 高電平數據穩定,低電平數據變化 SCL 時鐘線的占空比 */
  
    I2C_InitStructure.I2C_DutyCycle =  I2C_DutyCycle_2;
  
    I2C_InitStructure.I2C_OwnAddress1 =  (ucBoardAddr << 1);
  
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable  ;
  
    /* I2Cx的尋址模式 */
  
    I2C_InitStructure.I2C_AcknowledgedAddress  = I2C_AcknowledgedAddress_7bit;
  
    /* 通信速率 */
  
    I2C_InitStructure.I2C_ClockSpeed =  100000;  //Standard-mode devices at 100  Kbps
  
  
    /* I2Cx 初始化 */
  
    I2C_Init(CPS1848_I2C, &I2C_InitStructure);
  
  
    /* 使能 I2Cx 外設*/
  
    I2C_Cmd(CPS1848_I2C, ENABLE);
  
  
    /*使能或者失能指定的I2C應答功能*/
  
    I2C_AcknowledgeConfig(CPS1848_I2C,  ENABLE);
  
}
  
       I2C的讀操作實現如下——
  
BYTE  I2C_CPS1848_Read(uint8_t cps1848_index, uint32_t RegAddr, uint32_t *pdwData)
  
{
  
    volatile uint32_t i2cdata = 0;
  
    uint16_t NumByteToRead = 4;
  
  
    DWORD dwBusyCount = 0;
  
    const DWORD dwTimeOut = 1000;
  
  
    CHECK_POINTER_RET_ERR(pdwData);
  
  
    /* 等待busy */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_GetFlagStatus(CPS1848_I2C,  I2C_FLAG_BUSY), RESET, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* Enable Acknowledgement to be ready for  another reception
  
       necessary...*/
  
    I2C_AcknowledgeConfig(CPS1848_I2C,  ENABLE);
  
  
    /* 發送開始信號 */
  
    I2C_GenerateSTART(CPS1848_I2C, ENABLE);
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_MODE_SELECT), SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* 發送目標地址 : Reset the address bit0 for  write*/
  
    I2C_Send7bitAddress(CPS1848_I2C,  ((CPS1848_I2C_ADDR+cps1848_index) << 1), I2C_Direction_Transmitter);
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
  
    /* 發送寄存器地址--8bit */
  
    I2C_SendData(CPS1848_I2C, ((RegAddr>>18)  & 0xff));
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED), SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
    /* 發送寄存器地址--8bit */
  
    I2C_SendData(CPS1848_I2C,  ((RegAddr>>10) & 0xff));
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED), SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
    /* 發送寄存器地址--8bit */
  
     I2C_SendData(CPS1848_I2C, ((RegAddr>>2) & 0xff));
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED), SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
  
    /* 開始讀取數據 */
  
    I2C_GenerateSTART(CPS1848_I2C, ENABLE);
  
  
    /* Test on EV5 and clear it */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_MODE_SELECT), SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* 發送讀取的地址: Set the address bit0 for read*/
  
    I2C_Send7bitAddress(CPS1848_I2C,  ((CPS1848_I2C_ADDR+cps1848_index) << 1) | 0x01,  I2C_Direction_Receiver);
  
  
    /* Test on EV6 and clear it */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED), SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    while(NumByteToRead--)
  
    {
  
        if (0 == NumByteToRead)
  
        {
  
            /* 最后一個數據了,發送非應答信號(Disable Acknowledgement),結束傳輸;*/
  
             I2C_AcknowledgeConfig(CPS1848_I2C, DISABLE);
  
            /* 發送停止位 */
  
            I2C_GenerateSTOP(CPS1848_I2C,  ENABLE);
  
        }
  
  
        /* Test on EV7 and clear it */
  
        dwBusyCount = 0;
  
         CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_RECEIVED), SUCCESS, dwBusyCount, dwTimeOut);
  
        CHECK_WAIT_BUSY(dwBusyCount,  dwTimeOut);
  
        i2cdata = (i2cdata<<8) |  (I2C_ReceiveData(CPS1848_I2C));
  
    }
  
  
    *pdwData = i2cdata;
  
  
    /* Enable Acknowledgement to be ready for  another reception */
  
    I2C_AcknowledgeConfig(CPS1848_I2C,  ENABLE);
  
  
    return ERR_SUCCESS;
  
}
  
       I2C的寫操作實現如下——
  
BYTE  I2C_CPS1848_Write(uint8_t cps1848_index, uint32_t RegAddr, uint32_t RegData)
  
{
  
    DWORD dwBusyCount = 0;
  
    const DWORD dwTimeOut = 10000;
  
  
    /* 等待busy */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_GetFlagStatus(CPS1848_I2C,  I2C_FLAG_BUSY),
  
                         RESET, dwBusyCount,  dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
  
    /* 發送開始信號 */
  
    I2C_GenerateSTART(CPS1848_I2C, ENABLE);
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_MODE_SELECT),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
  
    /* 發送目標地址 */
  
    I2C_Send7bitAddress(CPS1848_I2C,  ((CPS1848_I2C_ADDR+cps1848_index) << 1), I2C_Direction_Transmitter);
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
  
    /* 發送寄存器地址--8bit */
  
    I2C_SendData(CPS1848_I2C,  ((RegAddr>>18) & 0xff));
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
    /* 發送寄存器地址--8bit */
  
    I2C_SendData(CPS1848_I2C,  ((RegAddr>>10) & 0xff));
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
    /* 發送寄存器地址--8bit */
  
    I2C_SendData(CPS1848_I2C,  ((RegAddr>>2) & 0xff));
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* 發送數據 */
  
    I2C_SendData(CPS1848_I2C,  ((RegData>>24) & 0xff));
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* 發送數據 */
  
    I2C_SendData(CPS1848_I2C,  ((RegData>>16) & 0xff));
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS, dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* 發送數據 */
  
    I2C_SendData(CPS1848_I2C,  ((RegData>>8) & 0xff));
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
    /* 發送數據 */
  
    I2C_SendData(CPS1848_I2C, ((RegData)  & 0xff));
  
  
    /* 等待握手 */
  
    dwBusyCount = 0;
  
    CHECK_BUSY(I2C_CheckEvent(CPS1848_I2C,  I2C_EVENT_MASTER_BYTE_TRANSMITTED),
  
                         SUCCESS,  dwBusyCount, dwTimeOut);
  
    CHECK_WAIT_BUSY(dwBusyCount, dwTimeOut);
  
  
  
    /* 發送停止位 */
  
    I2C_GenerateSTOP(CPS1848_I2C, ENABLE);
  
  
    return ERR_SUCCESS;
  
}
  
  


三、  軟件模擬I2C的實現
       GPIO軟件模擬I2C的實現中,首先是初始化;其次,由于沒有現存的I2C時序、協議、控制等API接口,這些就需要我們根據I2C協議進行嚴格實現,具體詳見附件。

四、  兩者的差異
       硬件I2C實現很簡潔,且流程中延時、應答等機制都無需軟件參與,只需做好正確配置和調用即可;軟件模擬I2C的實現需要非常完備,且需要嚴格按照I2C協議進行編寫,嚴格控制延時、應答、判定等。
       了解兩者之間的差異,并且能夠實現兩種I2C的使用,會極大加深我們對于I2C的理解和熟練程度,非常有益。


C文件51hei下載: gpio_i2c_bsp.rar (1.31 KB, 下載次數: 50)

評分

參與人數 1黑幣 +90 收起 理由
admin + 90 共享資料的黑幣獎勵!

查看全部評分

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

使用道具 舉報

沙發
ID:946578 發表于 2021-6-29 12:13 | 只看該作者
由于篇幅緣故,沒有將所有代碼貼在文中,而放在附件中了。
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 激情视频中文字幕 | 免费成人高清在线视频 | 欧美一区二区三区免费在线观看 | 成人在线视频免费观看 | 久久久久国产精品一区二区 | 色婷婷综合网 | 国产不卡在线 | 精品国产一级 | 日本网站免费观看 | www.成人久久 | 中文字幕在线观 | 日本中文字幕在线视频 | 精国产品一区二区三区 | 亚洲精品久久嫩草网站秘色 | 欧美日韩一区在线 | 欧美一区二区三区在线看 | 久久不射电影网 | 久久网一区二区三区 | 久久久亚洲一区 | 久草综合在线视频 | 欧美美女二区 | av中文字幕在线 | 久久美女网 | 男女羞羞视频在线免费观看 | 日本免费视频在线观看 | 色综合天天综合网国产成人网 | 91丨九色丨国产在线 | 99久久精品国产一区二区三区 | 国产a区| 福利精品| 亚洲精品乱码久久久久久蜜桃 | 精品亚洲视频在线 | 亚洲欧美国产精品一区二区 | 久久久久久高清 | 不卡在线视频 | 婷婷午夜天 | 久久久久久久国产精品影院 | 91免费电影 | 免费观看一级特黄欧美大片 | 黄色毛片在线看 | 天天综合网7799精品 |