0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于MM32生態(tài)實現(xiàn)I2C接口通訊的方式

GReq_mcu168 ? 來源:硬件攻城獅 ? 作者:硬件攻城獅 ? 2022-08-15 09:48 ? 次閱讀

概述:I2C通訊接口,是我們?nèi)粘弥惺褂米疃嗟?a target="_blank">MCU外設。最早在MCU沒有硬件I2C之前,都是通過GPIO口模擬I2C的時序來完成I2C通訊的。后來MCU帶有了I2C外設接口,其硬件I2C的使用也變成了日常。更主要的是,在節(jié)省MCU資源的同時,其操作也變得更加簡單和易用。再后來經(jīng)過市場需求的變化,開始有了支持I2C多從機地址通訊功能的MCU,讓I2C的應用緊跟市場需求。

雖然從I2C特性上知曉具有不同I2C地址的器件是可以掛載在同一個I2C總線上進行通訊的,但是,如果需要操作的I2C器件地址沖突呢?MCU的硬件I2C接口數(shù)量不夠呢?或者說MCU的I2C不支持從機多地址通訊功能呢?這時候,我們還是需要通過GPIO口來模擬I2C時序完成I2C主機/從機的功能。所以,并不是有了硬件I2C,軟件I2C就沒有發(fā)揮的空間了,恰恰是軟件和硬件這兩種實現(xiàn)方式共存互相補充。

對于I2C的基本概念及時序等知識點,本文不再詳細描述,大家可以下載附件中的《I2C總線概要》和《I2C總線規(guī)范》進行研究。 本文將通過如下四個方面,講述I2C在MM32F032/MM32F0140系列MCU上的實現(xiàn),以及使用I2C工具(圖莫斯USB2XXX總線適配器)進行實際測試:

硬件I2C主機通訊

軟件模擬I2C主機通訊

硬件I2C從機通訊

軟件模擬I2C從機通訊(有難度)

MM32F032系列MCU帶有1路硬件I2C接口,支持標準模式(數(shù)據(jù)傳輸速率為0~100kbps)和快速模式(數(shù)據(jù)最大傳輸速率為400kbps)兩種工作速率模式,其主要特征如下所示:

I2C總線協(xié)議轉(zhuǎn)換器/并行總線;

半雙工同步操作;

支持主從模式;

支持7位地址和10位地址;

支持標準模式100kbps、快速模式400kbps;

產(chǎn)生Start、Stop、Repeated Start,以及Acknowledge信號檢測;

在主機模式下只支持一個主機;

分別有2個字節(jié)的發(fā)送和接收緩沖;

在SCL和SDA上增加了無毛刺電路;

支持DAM、中斷和查詢操作方式;

MM32F0140系列MCU在MM32F032的基礎(chǔ)上I2C做了更豐富的功能,支持多從機地址通訊的功能、支持時鐘延展等等……具體的可以參考官方的數(shù)據(jù)手冊。

一、硬件I2C主機通訊

MM32的硬件I2C是我使用到現(xiàn)在,在代碼程序段操作最為簡潔的了;不需要再去考慮START信號、ACK信號,以及各種EVENT事件等……這些復雜的操作、或者是可以省略的操作都由官方的底層庫程序和芯片IP去實現(xiàn)了,讓我們在設計驅(qū)動程序時變量簡單了。 對于硬件I2C主機的配置,我們只需要復用的GPIO端口引腳、I2C通訊參數(shù),以及從機地址即可;然后就可以編程去讀寫I2C從機設備了,初始化配置及對I2C從機設備的讀寫操作的實現(xiàn)代碼如下:

void hI2C_MASTER_Init(uint8_t SlaveAddress){    GPIO_InitTypeDef GPIO_InitStructure;    I2C_InitTypeDef  I2C_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);
    I2C_StructInit(&I2C_InitStructure);    I2C_InitStructure.I2C_Mode       = I2C_Mode_MASTER;    I2C_InitStructure.I2C_OwnAddress = 0;    I2C_InitStructure.I2C_Speed      = I2C_Speed_STANDARD;    I2C_InitStructure.I2C_ClockSpeed = 100000;    I2C_Init(I2C1, &I2C_InitStructure);
    I2C_Send7bitAddress(I2C1, SlaveAddress, I2C_Direction_Transmitter);    I2C_Cmd(I2C1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_1);    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_1);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;    GPIO_Init(GPIOB, &GPIO_InitStructure);}
void hI2C_MASTER_Read(uint8_t Address, uint8_t *Buffer, uint8_t Length){    uint8_t flag = 0, count = 0;
    I2C_SendData(I2C1, Address);    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));
    for(uint8_t i = 0; i < Length; i++)    {        while(1)        {            if((I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFNF)) && (flag == 0))            {                I2C_ReadCmd(I2C1);   count++;                if(count == Length) flag = 1;            }
            if(I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_RFNE))            {                Buffer[i] = I2C_ReceiveData(I2C1);     break;            }        }    }
    I2C_GenerateSTOP(I2C1, ENABLE);    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));}
void hI2C_MASTER_Write(uint8_t Address, uint8_t *Buffer, uint8_t Length){    I2C_SendData(I2C1, Address);    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));
    for(uint8_t i = 0; i < Length; i++)    {        I2C_SendData(I2C1, *Buffer++);        while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));    }
    I2C_GenerateSTOP(I2C1, ENABLE);    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));}
void hI2C_MASTER_SHELL_Handler(uint8_t Mode){    uint8_t Buffer[10] = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0xAA};
    if(Mode == 1)    {        hI2C_MASTER_Write(0x00, Buffer, sizeof(Buffer));    }    else    {        hI2C_MASTER_Read(0x00, Buffer, sizeof(Buffer));
        printf("
hI2C Master Read : 
");
        for(uint8_t i = 0; i < sizeof(Buffer); i++)        {            printf("0x%02x ", Buffer[i]);        }
        printf("
");    }}SHELL_EXPORT_CMD(HI2C_MASTER, hI2C_MASTER_SHELL_Handler, Hardware I2C Master Read And Write);
實測結(jié)果如下所示: ff7c1c4c-1c29-11ed-ba43-dac502259ad0.png

二、軟件模擬I2C主機通訊

對于軟件模擬I2C主機通訊的實現(xiàn)方式,主要是通過操作GPIO端口引腳的高低電平,在滿足I2C通訊時序的要求上完成對I2C從機設備的讀寫操作;在實現(xiàn)軟件模擬I2C主機時,需要正確的產(chǎn)生Start起始條件、Stop停止條件,以及Restart重啟條件;需要在適當?shù)奈恢脤PIO端口引腳的輸入輸出狀態(tài)進行配置,以便能夠正確的判斷出ACK和NACK的應答信號;需要正確操作發(fā)送的字節(jié)格式,使地址內(nèi)容、數(shù)據(jù)內(nèi)容能夠被正確識別……

如下的軟件模擬I2C主機的實現(xiàn)方式通過定義了一個操作結(jié)構(gòu)體,通過傳遞操作實例的方式,讓軟件模擬I2C主機的程序?qū)崿F(xiàn)了面向?qū)ο蟮木幊?,借住同一段實現(xiàn)代碼,可以同時實現(xiàn)多個軟件模擬I2C主機通訊接口,在代碼實現(xiàn)上大大的節(jié)省了空間,同時也讓代碼的可移植性變得更加通用,具體的代碼實現(xiàn)如下所示:

typedef struct{    uint32_t      SCL_RCC;    GPIO_TypeDef *SCL_GPIO;    uint16_t      SCL_PIN;
    uint32_t      SDA_RCC;    GPIO_TypeDef *SDA_GPIO;    uint16_t      SDA_PIN;
    uint32_t      TIME;    uint8_t       SlaveAddress;} sI2C_MASTER_TypeDef;

sI2C_MASTER_TypeDef sI2C_MASTER = {    RCC_AHBENR_GPIOB, GPIOB, GPIO_Pin_6,    RCC_AHBENR_GPIOB, GPIOB, GPIO_Pin_7,    100,    0xA0};

#define sI2C_MASTER_SCL_H(sI2C)     GPIO_WriteBit(sI2C->SCL_GPIO, sI2C->SCL_PIN, Bit_SET)#define sI2C_MASTER_SCL_L(sI2C)     GPIO_WriteBit(sI2C->SCL_GPIO, sI2C->SCL_PIN, Bit_RESET)
#define sI2C_MASTER_SDA_H(sI2C)     GPIO_WriteBit(sI2C->SDA_GPIO, sI2C->SDA_PIN, Bit_SET)#define sI2C_MASTER_SDA_L(sI2C)     GPIO_WriteBit(sI2C->SDA_GPIO, sI2C->SDA_PIN, Bit_RESET)
#define sI2C_MASTER_SCL_GET(sI2C)   GPIO_ReadOutputDataBit(sI2C->SCL_GPIO, sI2C->SCL_PIN)#define sI2C_MASTER_SDA_GET(sI2C)   GPIO_ReadInputDataBit( sI2C->SDA_GPIO, sI2C->SDA_PIN)

void sI2C_MASTER_Delay(uint32_t Tick){    while(Tick--);}
void sI2C_MASTER_SDA_SetDirection(sI2C_MASTER_TypeDef *sI2C, uint8_t Direction){    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_AHBPeriphClockCmd(sI2C->SDA_RCC, ENABLE);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = sI2C->SDA_PIN;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    if(Direction)   /* Input */    {        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;    }    else            /* Output */    {        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;    }
    GPIO_Init(sI2C->SDA_GPIO, &GPIO_InitStructure);}
void sI2C_MASTER_SCL_SetDirection(sI2C_MASTER_TypeDef *sI2C, uint8_t Direction){    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_AHBPeriphClockCmd(sI2C->SCL_RCC, ENABLE);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = sI2C->SCL_PIN;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    if(Direction)   /* Input */    {        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;    }    else            /* Output */    {        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;    }
    GPIO_Init(sI2C->SCL_GPIO, &GPIO_InitStructure);}
void sI2C_MASTER_GenerateStart(sI2C_MASTER_TypeDef *sI2C){    sI2C_MASTER_SDA_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SDA_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SCL_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);}
void sI2C_MASTER_GenerateStop(sI2C_MASTER_TypeDef *sI2C){    sI2C_MASTER_SDA_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SDA_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);}
void sI2C_MASTER_PutACK(sI2C_MASTER_TypeDef *sI2C, uint8_t ack){    if(ack) sI2C_MASTER_SDA_H(sI2C);    /* NACK */    else    sI2C_MASTER_SDA_L(sI2C);    /* ACK  */
    sI2C_MASTER_Delay(sI2C->TIME);
    sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SCL_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);}
uint8_t sI2C_MASTER_GetACK(sI2C_MASTER_TypeDef *sI2C){    uint8_t ack = 0;
    sI2C_MASTER_SDA_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);
    sI2C_MASTER_SDA_SetDirection(sI2C, 1);
    sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);
    ack = sI2C_MASTER_SDA_GET(sI2C);
    sI2C_MASTER_SCL_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);
    sI2C_MASTER_SDA_SetDirection(sI2C, 0);
    return ack;}
uint8_t sI2C_MASTER_ReadByte(sI2C_MASTER_TypeDef *sI2C){    uint8_t Data = 0;
    sI2C_MASTER_SDA_H(sI2C); /* Must set SDA before read */
    sI2C_MASTER_SDA_SetDirection(sI2C, 1);
    for(uint8_t i = 0; i < 8; i++)    {        sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);
        Data <<= 1;
        if(sI2C_MASTER_SDA_GET(sI2C)) Data |= 0x01;
        sI2C_MASTER_SCL_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    }
    sI2C_MASTER_SDA_SetDirection(sI2C, 0);
    return Data;}
void sI2C_MASTER_WriteByte(sI2C_MASTER_TypeDef *sI2C, uint8_t Data){    for(uint8_t i = 0; i < 8; i++)    {        if(Data & 0x80) sI2C_MASTER_SDA_H(sI2C);        else            sI2C_MASTER_SDA_L(sI2C);
        Data <<= 1;
        sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);        sI2C_MASTER_SCL_L(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    }}
void sI2C_MASTER_Init(sI2C_MASTER_TypeDef *sI2C){    sI2C_MASTER_SDA_SetDirection(sI2C, 0);    sI2C_MASTER_SCL_SetDirection(sI2C, 0);
    sI2C_MASTER_SCL_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);    sI2C_MASTER_SDA_H(sI2C); sI2C_MASTER_Delay(sI2C->TIME);}
uint8_t sI2C_MASTER_Read(sI2C_MASTER_TypeDef *sI2C, uint8_t Address, uint8_t *Buffer, uint8_t Length){    if(Length == 0) return 0;
    sI2C_MASTER_GenerateStart(sI2C);
    sI2C_MASTER_WriteByte(sI2C, sI2C->SlaveAddress);
    if(sI2C_MASTER_GetACK(sI2C))    {        sI2C_MASTER_GenerateStop(sI2C); return 1;    }
    sI2C_MASTER_WriteByte(sI2C, Address);
    if(sI2C_MASTER_GetACK(sI2C))    {        sI2C_MASTER_GenerateStop(sI2C); return 1;    }
    sI2C_MASTER_GenerateStart(sI2C);
    sI2C_MASTER_WriteByte(sI2C, sI2C->SlaveAddress + 1);
    if(sI2C_MASTER_GetACK(sI2C))    {        sI2C_MASTER_GenerateStop(sI2C); return 1;    }
    while(1)    {        *Buffer++ = sI2C_MASTER_ReadByte(sI2C);
        if(--Length == 0)        {            sI2C_MASTER_PutACK(sI2C, 1); break;        }
        sI2C_MASTER_PutACK(sI2C, 0);    }
    sI2C_MASTER_GenerateStop(sI2C);
    return 0;}
uint8_t sI2C_MASTER_Write(sI2C_MASTER_TypeDef *sI2C, uint8_t Address, uint8_t *Buffer, uint8_t Length){    uint8_t i = 0;
    if(Length == 0) return 0;
    sI2C_MASTER_GenerateStart(sI2C);
    sI2C_MASTER_WriteByte(sI2C, sI2C->SlaveAddress);
    if(sI2C_MASTER_GetACK(sI2C))    {        sI2C_MASTER_GenerateStop(sI2C); return 1;    }
    sI2C_MASTER_WriteByte(sI2C, Address);
    if(sI2C_MASTER_GetACK(sI2C))    {        sI2C_MASTER_GenerateStop(sI2C); return 1;    }
    for(i = 0; i < Length; i++)    {        sI2C_MASTER_WriteByte(sI2C, *Buffer++);
        if(sI2C_MASTER_GetACK(sI2C))     break;    }
    sI2C_MASTER_GenerateStop(sI2C);
    if(i == Length) return 0;    else            return 1;}
void sI2C_MASTER_SHELL_Handler(uint8_t Mode){    uint8_t Buffer[10] = {0x11, 0x22, 0x33, 0x44, 0x55, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE};
    if(Mode == 1)    {        sI2C_MASTER_Write(&sI2C_MASTER, 0x00, Buffer, sizeof(Buffer));    }    else    {        sI2C_MASTER_Read(&sI2C_MASTER, 0x00, Buffer, sizeof(Buffer));
        printf("
sI2C Master Read : 
");
        for(uint8_t i = 0; i < sizeof(Buffer); i++)        {            printf("0x%02x ", Buffer[i]);        }
        printf("
");    }}SHELL_EXPORT_CMD(SI2C_MASTER, sI2C_MASTER_SHELL_Handler, Software I2C Master Read And Write);
實測結(jié)果如下所示: ff9a34fc-1c29-11ed-ba43-dac502259ad0.png

三、硬件I2C從機通訊

對于硬件I2C從機通訊來說,更多的是采用中斷的響應方式來避免程序在某一處一直等待I2C主機的操作;而輪詢的方式很容易捕捉不到I2C的請求或者事件;所以如下硬件I2C從機通訊的方式使用的就是中斷處理方式,I2C主機任何操作和請求都會映射成對應的中斷,待從機檢測到了之后,進入中斷進行相應的處理,同時中斷的方式也保證了通訊的正常和穩(wěn)定性。

現(xiàn)在市面上很多MCU的I2C從機模式都支持多地址模式,但每家的IP功能設計都不一樣:有些是直接通過寄存器設置從機地址方式,這種方式限制了所支持從機地址的個數(shù);有些是通過地址掩碼的方式(類似于CAN通訊的ID濾波器),通過逐位比較的方式來判別所支持的I2C從機地址,這種方式可以支持很多個從機地址;第二種方式相比于第一種實現(xiàn)方式更靈活,支持的從機設備地址也更多!

MM32F032不支持多地址從機功能,但MM32F0140支持從機多地址通訊,可以根據(jù)實際項目需求選擇對應的芯片型號;從機多地址功能采用的是地址掩碼方式來過濾從機地址的,這樣可以支持更多的從機設備地址;通過設置從機設備地址和從機地址掩碼來實現(xiàn)從機多地址通訊功能;硬件I2C從機通訊具體的代碼實現(xiàn)如下:

void hI2C_SLAVE_Init(uint8_t SlaveAddress){    I2C_InitTypeDef  I2C_InitStructure;    GPIO_InitTypeDef GPIO_InitStructure;    NVIC_InitTypeDef NVIC_InitStructure;
    QUEUE_INIT(QUEUE_HI2C_SLAVE_IDX);
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);
    I2C_StructInit(&I2C_InitStructure);    I2C_InitStructure.Mode       = I2C_Mode_SLAVE;    I2C_InitStructure.OwnAddress = 0;    I2C_InitStructure.Speed      = I2C_Speed_FAST;    I2C_InitStructure.ClockSpeed = 400000;    I2C_Init(I2C1, &I2C_InitStructure);
    I2C_ITConfig(I2C1, I2C_IT_RD_REQ,  ENABLE);    I2C_ITConfig(I2C1, I2C_IT_RX_FULL, ENABLE);
    I2C_Cmd(I2C1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_1);    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_1);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_FLOATING;    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;    GPIO_Init(GPIOB, &GPIO_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_IRQn;    NVIC_InitStructure.NVIC_IRQChannelPriority = 0;    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);
    I2C_SendSlaveAddress(I2C1, SlaveAddress);}
void I2C1_IRQHandler(void){    static uint8_t Data = 0;
    if(I2C_GetITStatus(I2C1, I2C_IT_RD_REQ)  != RESET)    {        I2C_ClearITPendingBit(I2C1, I2C_IT_RD_REQ);
        while(1)        {            I2C_SendData(I2C1, Data++);            while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TX_EMPTY) == RESET);
            if((Data % 10) == 0)            {                I2C_GenerateSTOP(I2C1, ENABLE); break;            }        }    }
    if(I2C_GetITStatus(I2C1, I2C_IT_RX_FULL) != RESET)    {        QUEUE_WRITE(QUEUE_HI2C_SLAVE_IDX, I2C_ReceiveData(I2C1));    }}
實測結(jié)果如下所示: ffa8ad34-1c29-11ed-ba43-dac502259ad0.png

四、軟件模擬I2C從機通訊

軟件模擬I2C從機通訊是I2C通訊時序逆向的實現(xiàn)過程,它需要通過捕捉I2C主機的信號時序?qū)χ鳈C的事件、請求,以及發(fā)送過來的數(shù)據(jù)進行解析,又要正確的回復I2C主機,所以它的實現(xiàn)方式比I2C模擬主機完全不同。這需要開發(fā)者對I2C時序十分熟悉,所以在研讀下面軟件模擬I2C從機通訊程序時,建議對照I2C時序一點點分析(提示:這部分內(nèi)容有點難度)。

對于軟件模擬I2C從機通訊的實現(xiàn)是通過兩個GPIO端口引腳分別與I2C主機的SCL和SDA進行連接,程序中將這兩個GPIO端口引腳配置成外部中斷EXTI工作模式,通過捕獲GPIO端口引腳的上升沿、下降沿,以及高低電平狀態(tài),配合軟件模擬I2C從機的狀態(tài)管理,實現(xiàn)與I2C主機之間的通訊功能,在如下的程序中添加了詳細的注釋和說明,方便大家閱讀和理解,具體的代碼實現(xiàn)如下:

typedef struct{    uint32_t      SCL_RCC;    GPIO_TypeDef *SCL_GPIO;    uint16_t      SCL_PIN;
    uint8_t       SCL_EXTI_PortSource;    uint8_t       SCL_EXTI_PinSource;    uint32_t      SCL_EXTI_Line;
    uint32_t      SDA_RCC;    GPIO_TypeDef *SDA_GPIO;    uint16_t      SDA_PIN;
    uint8_t       SDA_EXTI_PortSource;    uint8_t       SDA_EXTI_PinSource;    uint32_t      SDA_EXTI_Line;
    uint8_t       SlaveAddress;} sI2C_SLAVE_TypeDef;

sI2C_SLAVE_TypeDef sI2C_SLAVE = {    RCC_AHBENR_GPIOB, GPIOB, GPIO_Pin_6, EXTI_PortSourceGPIOB, EXTI_PinSource6, EXTI_Line6,    RCC_AHBENR_GPIOB, GPIOB, GPIO_Pin_7, EXTI_PortSourceGPIOB, EXTI_PinSource7, EXTI_Line7,    0xA0,};

#define sI2C_SLAVE_STATE_NA        0#define sI2C_SLAVE_STATE_STA       1#define sI2C_SLAVE_STATE_ADD       2#define sI2C_SLAVE_STATE_ADD_ACK   3#define sI2C_SLAVE_STATE_DAT       4#define sI2C_SLAVE_STATE_DAT_ACK   5#define sI2C_SLAVE_STATE_STO       6
uint8_t sI2C_SLAVE_State = sI2C_SLAVE_STATE_NA;
uint8_t sI2C_SLAVE_ShiftCounter = 0;uint8_t sI2C_SLAVE_SlaveAddress = 0;uint8_t sI2C_SLAVE_ReceivedData = 0;uint8_t sI2C_SLAVE_TransmitData = 0x50;
uint8_t sI2C_SLAVE_TransmitBuffer[16] = {    0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,    0x89, 0x9A, 0xAB, 0xBC, 0xCD, 0xDE, 0xEF, 0xF0,};uint8_t sI2C_SLAVE_TransmitIndex = 0;

bool sI2C_SLAVE_READ_SCL(sI2C_SLAVE_TypeDef *sI2C){    return GPIO_ReadInputDataBit(sI2C->SCL_GPIO, sI2C->SCL_PIN);}
bool sI2C_SLAVE_READ_SDA(sI2C_SLAVE_TypeDef *sI2C){    return GPIO_ReadInputDataBit(sI2C->SDA_GPIO, sI2C->SDA_PIN);}

/******************************************************************************* * [url=home.php?mod=space&uid=247401]@brief[/url]       配置模擬I2C的GPIO端口, 默認設置成輸入模式, 并使能相應的外部觸發(fā) *              中斷功能(上升沿和下降沿) * @param        * @retval       * [url=home.php?mod=space&uid=93590]@Attention[/url]   *******************************************************************************/void sI2C_SLAVE_Init(sI2C_SLAVE_TypeDef *sI2C){    GPIO_InitTypeDef GPIO_InitStructure;    EXTI_InitTypeDef EXTI_InitStructure;    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_AHBPeriphClockCmd(sI2C->SCL_RCC, ENABLE);    RCC_AHBPeriphClockCmd(sI2C->SDA_RCC, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = sI2C->SCL_PIN;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;    GPIO_Init(sI2C->SCL_GPIO, &GPIO_InitStructure);
    GPIO_StructInit(&GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = sI2C->SDA_PIN;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;    GPIO_Init(sI2C->SDA_GPIO, &GPIO_InitStructure);
    SYSCFG_EXTILineConfig(sI2C->SCL_EXTI_PortSource, sI2C->SCL_EXTI_PinSource);
    EXTI_StructInit(&EXTI_InitStructure);    EXTI_InitStructure.EXTI_Line    = sI2C->SCL_EXTI_Line;    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;    EXTI_InitStructure.EXTI_LineCmd = ENABLE;    EXTI_Init(&EXTI_InitStructure);
    SYSCFG_EXTILineConfig(sI2C->SDA_EXTI_PortSource, sI2C->SDA_EXTI_PinSource);
    EXTI_StructInit(&EXTI_InitStructure);    EXTI_InitStructure.EXTI_Line    = sI2C->SDA_EXTI_Line;    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;    EXTI_InitStructure.EXTI_LineCmd = ENABLE;    EXTI_Init(&EXTI_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = EXTI4_15_IRQn;    NVIC_InitStructure.NVIC_IRQChannelPriority  = 0x00;    NVIC_InitStructure.NVIC_IRQChannelCmd     = ENABLE;    NVIC_Init(&NVIC_InitStructure);}

/******************************************************************************* * [url=home.php?mod=space&uid=247401]@brief[/url]       設置SDA信號線的輸入輸出方便, 0代表Output輸出, 1代表Input輸入 * @param        * @retval       * [url=home.php?mod=space&uid=93590]@Attention[/url]   *******************************************************************************/void sI2C_SLAVE_SDA_SetDirection(sI2C_SLAVE_TypeDef *sI2C, uint8_t Direction){    GPIO_InitTypeDef GPIO_InitStructure;
    if(Direction)   /* Input */    {        GPIO_StructInit(&GPIO_InitStructure);        GPIO_InitStructure.GPIO_Pin   = sI2C->SDA_PIN;        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;        GPIO_Init(sI2C->SDA_GPIO, &GPIO_InitStructure);    }    else            /* Output */    {        GPIO_StructInit(&GPIO_InitStructure);        GPIO_InitStructure.GPIO_Pin   = sI2C->SDA_PIN;        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_OD;        GPIO_Init(sI2C->SDA_GPIO, &GPIO_InitStructure);    }}

/****************************************************************************** * @brief       設置SDA信號線的輸出電平(高電平 / 低電平) * @param        * @retval       * @attention   ******************************************************************************/void sI2C_SLAVE_SDA_SetLevel(sI2C_SLAVE_TypeDef *sI2C, uint8_t Level){    sI2C_SLAVE_SDA_SetDirection(sI2C, 0);
    if(Level)    {        GPIO_WriteBit(sI2C->SDA_GPIO, sI2C->SDA_PIN, Bit_SET);    }    else    {        GPIO_WriteBit(sI2C->SDA_GPIO, sI2C->SDA_PIN, Bit_RESET);    }}

/****************************************************************************** * @brief       當SCL觸發(fā)上升沿外部中斷時的處理 * @param        * @retval       * @attention   ******************************************************************************/void sI2C_SLAVE_SCL_RiseHandler(sI2C_SLAVE_TypeDef *sI2C){    /* SCL為上升沿, 數(shù)據(jù)鎖定, 主從機從SDA總線上獲取數(shù)據(jù)位 */    switch(sI2C_SLAVE_State)    {        case sI2C_SLAVE_STATE_ADD:
            /* I2C發(fā)送遵義MSB, 先發(fā)送高位, 再發(fā)送低位, 所以在接收的時候, 數(shù)據(jù)進行左移 */
            sI2C_SLAVE_SlaveAddress <<= 1;            sI2C_SLAVE_ShiftCounter  += 1;
            if(sI2C_SLAVE_READ_SDA(sI2C) ==  Bit_SET)            {                sI2C_SLAVE_SlaveAddress |= 0x01;            }
            /* 當接收到8位地址位后, 從機需要在第9個時鐘給出ACK應答, 等待SCL下降沿的時候給出ACK信號 */            if(sI2C_SLAVE_ShiftCounter == 8)            {                sI2C_SLAVE_State = sI2C_SLAVE_STATE_ADD_ACK;            }            break;
        case sI2C_SLAVE_STATE_ADD_ACK:            /* 從機地址的ACK回復后, 切換到收發(fā)數(shù)據(jù)狀態(tài) */            sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT;
            sI2C_SLAVE_ShiftCounter = 0;    /* 數(shù)據(jù)移位計數(shù)器清零 */            sI2C_SLAVE_ReceivedData = 0;    /* sI2C_SLAVE的接收數(shù)據(jù)清零 */            break;
        case sI2C_SLAVE_STATE_DAT:            if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)            {                /* 主機寫操作:此時從機應該獲取主機發(fā)送的SDA信號線電平狀態(tài), 進行位存儲 */                sI2C_SLAVE_ReceivedData <<= 1;                sI2C_SLAVE_ShiftCounter  += 1;
                if(sI2C_SLAVE_READ_SDA(sI2C) == Bit_SET)                {                    sI2C_SLAVE_ReceivedData |= 0x01;                }
                /* 當收到一個完整的8位數(shù)據(jù)時, 將收到的數(shù)據(jù)存放到I2C接收消息隊列中, 狀態(tài)轉(zhuǎn)換到給主機發(fā)送ACK應答 */                if(sI2C_SLAVE_ShiftCounter == 8)                {                    QUEUE_WRITE(QUEUE_SI2C_SLAVE_IDX, sI2C_SLAVE_ReceivedData);
                    sI2C_SLAVE_ShiftCounter = 0;    /* 數(shù)據(jù)移位計數(shù)器清零 */                    sI2C_SLAVE_ReceivedData = 0;    /* sI2C_SLAVE的接收數(shù)據(jù)清零 */
                    sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT_ACK;                }            }            else            {                /* 主機讀操作:在SCL上升沿的時候, 主機獲取當前SDA的狀態(tài)位, 如果到了第8個數(shù)位的上升沿,                 * 那接下來就是主機回復從機的應答或非應答信號了, 所以將狀態(tài)切換到等待ACK的狀態(tài), 同時準備下一個需要發(fā)送的數(shù)據(jù)                 */                if(sI2C_SLAVE_ShiftCounter == 8)                {                    sI2C_SLAVE_ShiftCounter = 0;    /* sI2C_SLAVE的接收數(shù)據(jù)清零 */                    sI2C_SLAVE_TransmitData = sI2C_SLAVE_TransmitBuffer[sI2C_SLAVE_TransmitIndex++];
                    sI2C_SLAVE_TransmitIndex %= 16;
                    sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT_ACK;                }            }            break;
        case sI2C_SLAVE_STATE_DAT_ACK:            if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)            {                /* 主機寫操作:從機發(fā)送ACK, 等待主機讀取從機發(fā)送的ACK信號 */
                sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT;  /* 狀態(tài)切換到數(shù)據(jù)接收狀態(tài) */            }            else            {                /* 主機讀操作:主機發(fā)送ACK, 從機可以讀取主機發(fā)送的ACK信號 */
                uint8_t ack = sI2C_SLAVE_READ_SDA(sI2C);
                if(ack == Bit_RESET)                {                    sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT;    /* 接收到 ACK, 繼續(xù)發(fā)送數(shù)據(jù) */                }                else                {                    sI2C_SLAVE_State = sI2C_SLAVE_STATE_STO;    /* 接收到NACK, 停止發(fā)送數(shù)據(jù) */                }            }            break;
        default:            break;    }}

/****************************************************************************** * @brief       當SCL觸發(fā)下降沿外部中斷時的處理 * @param        * @retval       * @attention   ******************************************************************************/void sI2C_SLAVE_SCL_FallHandler(sI2C_SLAVE_TypeDef *sI2C){    /* SCL為下降沿, 數(shù)據(jù)可變 */    switch(sI2C_SLAVE_State)    {        case sI2C_SLAVE_STATE_STA:            /*             * 檢測到START信號后, SCL第一個下降沿表示開始傳輸Slave Address,             * 根據(jù)數(shù)據(jù)有效性的規(guī)則, 地址的第一位需要等到SCL變?yōu)楦唠娖綍r才可以讀取             * 切換到獲取Slave Address的狀態(tài), 等待SCL的上升沿觸發(fā)             */            sI2C_SLAVE_State = sI2C_SLAVE_STATE_ADD;
            sI2C_SLAVE_ShiftCounter = 0;    /* 數(shù)據(jù)移位計數(shù)器清零 */            sI2C_SLAVE_SlaveAddress = 0;    /* sI2C_SLAVE的從機地址清零 */            sI2C_SLAVE_ReceivedData = 0;    /* sI2C_SLAVE的接收數(shù)據(jù)清零 */            break;
        case sI2C_SLAVE_STATE_ADD:            /*             * 在主機發(fā)送Slave Address的時候, 從機只是讀取SDA狀態(tài), 進行地址解析, 所以這邊沒有處理             */            break;
        case sI2C_SLAVE_STATE_ADD_ACK:
            /* SCL低電平的時候, 給I2C總線發(fā)送地址的應答信號, 狀態(tài)不發(fā)生改變, 等待下一個上升沿將ACK發(fā)送出去 */
            sI2C_SLAVE_SDA_SetLevel(sI2C, 0);   /* 將SDA信號拉低, 向主機發(fā)送ACK信號 */            break;
        case sI2C_SLAVE_STATE_DAT:
            /* 在SCL時鐘信號的下降沿, SDA信號線處理可變的狀態(tài) */
            if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)            {                /* 主機寫操作:將SDA信號線設置成獲取狀態(tài), 等待下一個SCL上升沿時獲取數(shù)據(jù)位 */                sI2C_SLAVE_SDA_SetDirection(sI2C, 1);            }            else            {                /* 主機讀操作:根據(jù)發(fā)送的數(shù)據(jù)位設置SDA信號線的輸出電平, 等待下一個SCL上升沿時發(fā)送數(shù)據(jù)位 */                if(sI2C_SLAVE_TransmitData & 0x80)                {                    sI2C_SLAVE_SDA_SetLevel(sI2C, 1);                }                else                {                    sI2C_SLAVE_SDA_SetLevel(sI2C, 0);                }
                sI2C_SLAVE_TransmitData <<= 1;                sI2C_SLAVE_ShiftCounter  += 1;            }            break;
        case sI2C_SLAVE_STATE_DAT_ACK:
            /* 在第8個SCL時鐘信號下降沿的處理 */
            if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)            {                /* 主機寫操作:從機在接收到數(shù)據(jù)后, 需要給主機一個ACK應答信號, 狀態(tài)不發(fā)生改變, 等待下一個上升沿將ACK發(fā)送出去 */
                sI2C_SLAVE_SDA_SetLevel(sI2C, 0);   /* 將SDA信號拉低, 向主機發(fā)送ACK信號 */            }            else            {                /* 主機讀操作:從機需要釋放當前的SDA信號線, 以便主機發(fā)送ACK或NACK給從機, 狀態(tài)不發(fā)生改變, 等待下一個上升沿讀取ACK信號 */                sI2C_SLAVE_SDA_SetDirection(sI2C, 1);            }            break;
        default:            break;    }}

/**  * @brief  當SDA觸發(fā)上升沿外部中斷時的處理  * @param  None  * @retval None  */void sI2C_SLAVE_SDA_RiseHandler(sI2C_SLAVE_TypeDef *sI2C){    if(sI2C_SLAVE_READ_SCL(sI2C) == Bit_SET)    /* SCL為高時,SDA為上升沿:STOP */    {        sI2C_SLAVE_State = sI2C_SLAVE_STATE_STO;    }    else                                        /* SCL為低時,SDA為上升沿:數(shù)據(jù)的變化 */    {    }}

/**  * @brief  當SDA觸發(fā)下降沿外部中斷時的處理  * @param  None  * @retval None  */void sI2C_SLAVE_SDA_FallHandler(sI2C_SLAVE_TypeDef *sI2C){    if(sI2C_SLAVE_READ_SCL(sI2C) == Bit_SET)    /* SCL為高時,SDA為下降沿:START */    {        sI2C_SLAVE_State = sI2C_SLAVE_STATE_STA;    }    else                                        /* SCL為低時,SDA為下降沿:數(shù)據(jù)的變化 */    {    }}

/******************************************************************************* * @brief        * @param        * @retval       * @attention   *******************************************************************************/void EXTI4_15_IRQHandler(void){    /* I2C SCL */    if(EXTI_GetITStatus(sI2C_SLAVE.SCL_EXTI_Line) != RESET)    {        if(sI2C_SLAVE_READ_SCL(&sI2C_SLAVE) == Bit_SET)        {            sI2C_SLAVE_SCL_RiseHandler(&sI2C_SLAVE);        }        else        {            sI2C_SLAVE_SCL_FallHandler(&sI2C_SLAVE);        }
        EXTI_ClearITPendingBit(sI2C_SLAVE.SCL_EXTI_Line);    }
    /* I2C SDA */    if(EXTI_GetITStatus(sI2C_SLAVE.SDA_EXTI_Line) != RESET)    {        if(sI2C_SLAVE_READ_SDA(&sI2C_SLAVE) == Bit_SET)        {            sI2C_SLAVE_SDA_RiseHandler(&sI2C_SLAVE);        }        else        {            sI2C_SLAVE_SDA_FallHandler(&sI2C_SLAVE);        }
        EXTI_ClearITPendingBit(sI2C_SLAVE.SDA_EXTI_Line);    }}
實測結(jié)果如下所示:

ffa8ad34-1c29-11ed-ba43-dac502259ad0.png

以上就是基于MM32生態(tài)實現(xiàn)I2C接口通訊的幾種方式了

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 硬件
    +關(guān)注

    關(guān)注

    11

    文章

    3161

    瀏覽量

    66003
  • 接口通訊
    +關(guān)注

    關(guān)注

    0

    文章

    5

    瀏覽量

    6168
  • I2C接口
    +關(guān)注

    關(guān)注

    1

    文章

    124

    瀏覽量

    25076
  • MM32
    +關(guān)注

    關(guān)注

    1

    文章

    106

    瀏覽量

    721

原文標題:I2C接口通訊實現(xiàn)方式,你都掌握了嗎?

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    MM32F013x上實現(xiàn)I2C多地址的功能應用

    I2C多地址的功能應用。 1.配置方式 MM32F013X的多地址功能是通過配置I2C\_SLAVMASK寄存器來實現(xiàn)的。通過
    發(fā)表于 01-05 11:43 ?2042次閱讀

    I2CMM32F032/MM32F0140系列MCU上的實現(xiàn)

    概述:I2C通訊接口,是我們?nèi)粘弥惺褂米疃嗟腗CU外設。最早在MCU沒有硬件I2C之前,都是通過GPIO口模擬I2C的時序來完成
    發(fā)表于 09-22 14:22 ?718次閱讀

    有哪幾種方式可以實現(xiàn)I2C通訊

    I2C協(xié)議自己百度查兩種方式實現(xiàn)I2C 通訊:一種使用GPIO口來模擬I2C 協(xié)議,一種是使用s
    發(fā)表于 01-13 06:43

    I2C總線通信接口的CPLD實現(xiàn)

    介紹采用ALTERA 公司的可編程器件,實現(xiàn)I2C 總線的通信接口的基本原理; 給出部分VHDL語言描述。該通信接口與專用的接口芯片相比,
    發(fā)表于 05-14 13:16 ?26次下載

    I2C總線接口模塊設計

    本實驗是基于EasyFPGA030的I2C總線接口模塊設計,用EasyFPGA030開發(fā)套件通過I2C協(xié)議實現(xiàn)對二線制I2C串行EEPROM
    發(fā)表于 11-02 17:01 ?41次下載

    基于I2C總線的大型開關(guān)矩陣設計與實現(xiàn)

    基于I2C 總線的大型開關(guān)矩陣設計與實現(xiàn)作者:王振 劉耀周 劉煥照摘要:本文介紹了一種采用 USB 接口,利用I2C 總線傳輸數(shù)據(jù),由CP
    發(fā)表于 02-06 10:42 ?44次下載

    基于CPLD的I2C總線接口設計

    在電路設計中,I2C總線是比較常用的兩線式串行通信方式,大多數(shù)的CPU都擅長于并口操作,不具備直接操作I2C總線接口的能力。為了使不具備I2C
    發(fā)表于 02-12 16:11 ?95次下載
    基于CPLD的<b class='flag-5'>I2C</b>總線<b class='flag-5'>接口</b>設計

    I2C 接口進入 Busy 狀態(tài)不能退出

    I2C 接口進入 Busy 狀 態(tài)不能退出
    發(fā)表于 12-08 11:44 ?0次下載

    STM32F2—通信接口I2C

    STM32F2—通信接口I2C
    發(fā)表于 09-03 11:24 ?31次下載
    STM32F<b class='flag-5'>2</b>—通信<b class='flag-5'>接口</b><b class='flag-5'>I2C</b>

    LPC1100 I2C 接口技術(shù)與可靠性設計(I2C器件應用方案

    LPC1100 I2C 接口技術(shù)與可靠性設計(I2C器件應用方案
    發(fā)表于 09-29 19:11 ?6次下載
    LPC1100 <b class='flag-5'>I2C</b> <b class='flag-5'>接口</b>技術(shù)與可靠性設計(<b class='flag-5'>I2C</b>器件應用方案

    LPC1100 I2C 接口技術(shù)與可靠性設計I2C器件應用方案

    LPC1100 I2C 接口技術(shù)與可靠性設計I2C器件應用方案
    發(fā)表于 10-09 09:26 ?8次下載
    LPC1100 <b class='flag-5'>I2C</b> <b class='flag-5'>接口</b>技術(shù)與可靠性設計<b class='flag-5'>I2C</b>器件應用方案

    I2C LCD 器件通過驅(qū)動帶有 I2C 接口2 線式 16 字符 LCD

    I2C LCD 器件通過驅(qū)動帶有 I2C 接口2 線式 16 字符 LCD
    發(fā)表于 10-10 08:22 ?13次下載
    <b class='flag-5'>I2C</b> LCD 器件通過驅(qū)動帶有 <b class='flag-5'>I2C</b> <b class='flag-5'>接口</b>的 <b class='flag-5'>2</b> 線式 16 字符 LCD

    詳細介紹GPIO、I2C、SPI通訊原理以及物理層原理

    6. I2C接口工作模式7. 硬件拉高拉低的過程8. 一對多9. 開發(fā)流程三. SPI1. 什么是SPI?2. SPI優(yōu)與缺點3. SPI組成原理4. SPI通訊模式5.
    發(fā)表于 12-05 12:51 ?21次下載
    詳細介紹GPIO、<b class='flag-5'>I2C</b>、SPI<b class='flag-5'>通訊</b>原理以及物理層原理

    I2C接口配置ES7243錄音芯片,MCU(STM32)收不到I2C ACK的問題

    I2C接口配置ES7243錄音芯片,MCU(STM32)收不到I2C ACK的問題
    發(fā)表于 12-08 16:36 ?10次下載
    <b class='flag-5'>I2C</b><b class='flag-5'>接口</b>配置ES7243錄音芯片,MCU(STM32)收不到<b class='flag-5'>I2C</b> ACK的問題

    基于MM32生態(tài)實現(xiàn)I2C接口通訊的幾種方式

    MM32F032系列MCU帶有1路硬件I2C接口,支持標準模式(數(shù)據(jù)傳輸速率為0~100kbps)和快速模式(數(shù)據(jù)最大傳輸速率為400kbps)兩種工作速率模式,其主要特征如下所示:
    的頭像 發(fā)表于 09-26 14:19 ?1653次閱讀