雖然從I2C特性上知曉具有不同I2C地址的器件是可以掛載在同一個(gè)I2C總線上進(jìn)行通訊的,但是,如果需要操作的I2C器件地址沖突呢?MCU的硬件I2C接口數(shù)量不夠呢?或者說MCU的I2C不支持從機(jī)多地址通訊功能呢?這時(shí)候,我們還是需要通過GPIO口來模擬I2C時(shí)序完成I2C主機(jī)/從機(jī)的功能。所以,并不是有了硬件I2C,軟件I2C就沒有發(fā)揮的空間了,恰恰是軟件和硬件這兩種實(shí)現(xiàn)方式共存互相補(bǔ)充。
?
對(duì)于I2C的基本概念及時(shí)序等知識(shí)點(diǎn),本文不再詳細(xì)描述,大家可以下載附件中的《I2C總線概要》和《I2C總線規(guī)范》進(jìn)行研究。本文將通過如下四個(gè)方面,講述I2C在MM32F032/MM32F0140系列MCU上的實(shí)現(xiàn),以及使用I2C工具(圖莫斯USB2XXX總線適配器)進(jìn)行實(shí)際測(cè)試:- 硬件I2C主機(jī)通訊
- 軟件模擬I2C主機(jī)通訊
- 硬件I2C從機(jī)通訊
- 軟件模擬I2C從機(jī)通訊(有難度)
- I2C總線協(xié)議轉(zhuǎn)換器/并行總線;
- 半雙工同步操作;
- 支持主從模式;
- 支持7位地址和10位地址;
- 支持標(biāo)準(zhǔn)模式100kbps、快速模式400kbps;
- 產(chǎn)生Start、Stop、Repeated Start,以及Acknowledge信號(hào)檢測(cè);
- 在主機(jī)模式下只支持一個(gè)主機(jī);
- 分別有2個(gè)字節(jié)的發(fā)送和接收緩沖;
- 在SCL和SDA上增加了無毛刺電路;
- 支持DAM、中斷和查詢操作方式;
- MM32F0140系列MCU在MM32F032的基礎(chǔ)上I2C做了更豐富的功能,支持多從機(jī)地址通訊的功能、支持時(shí)鐘延展等等……具體的可以參考官方的數(shù)據(jù)手冊(cè)。
?
一、硬件I2C主機(jī)通訊
MM32的硬件I2C是我使用到現(xiàn)在,在代碼程序段操作最為簡(jiǎn)潔的了;不需要再去考慮START信號(hào)、ACK信號(hào),以及各種EVENT事件等……這些復(fù)雜的操作、或者是可以省略的操作都由官方的底層庫程序和芯片IP去實(shí)現(xiàn)了,讓我們?cè)谠O(shè)計(jì)驅(qū)動(dòng)程序時(shí)變量簡(jiǎn)單了。對(duì)于硬件I2C主機(jī)的配置,我們只需要復(fù)用的GPIO端口引腳、I2C通訊參數(shù),以及從機(jī)地址即可;然后就可以編程去讀寫I2C從機(jī)設(shè)備了,初始化配置及對(duì)I2C從機(jī)設(shè)備的讀寫操作的實(shí)現(xiàn)代碼如下:- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
實(shí)測(cè)結(jié)果如下所示: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);
?
二、軟件模擬I2C主機(jī)通訊
對(duì)于軟件模擬I2C主機(jī)通訊的實(shí)現(xiàn)方式,主要是通過操作GPIO端口引腳的高低電平,在滿足I2C通訊時(shí)序的要求上完成對(duì)I2C從機(jī)設(shè)備的讀寫操作;在實(shí)現(xiàn)軟件模擬I2C主機(jī)時(shí),需要正確的產(chǎn)生Start起始條件、Stop停止條件,以及Restart重啟條件;需要在適當(dāng)?shù)奈恢脤?duì)GPIO端口引腳的輸入輸出狀態(tài)進(jìn)行配置,以便能夠正確的判斷出ACK和NACK的應(yīng)答信號(hào);需要正確操作發(fā)送的字節(jié)格式,使地址內(nèi)容、數(shù)據(jù)內(nèi)容能夠被正確識(shí)別……
如下的軟件模擬I2C主機(jī)的實(shí)現(xiàn)方式通過定義了一個(gè)操作結(jié)構(gòu)體,通過傳遞操作實(shí)例的方式,讓軟件模擬I2C主機(jī)的程序?qū)崿F(xiàn)了面向?qū)ο蟮木幊蹋枳⊥欢螌?shí)現(xiàn)代碼,可以同時(shí)實(shí)現(xiàn)多個(gè)軟件模擬I2C主機(jī)通訊接口,在代碼實(shí)現(xiàn)上大大的節(jié)省了空間,同時(shí)也讓代碼的可移植性變得更加通用,具體的代碼實(shí)現(xiàn)如下所示:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
實(shí)測(cè)結(jié)果如下所示: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);
?
三、硬件I2C從機(jī)通訊
對(duì)于硬件I2C從機(jī)通訊來說,更多的是采用中斷的響應(yīng)方式來避免程序在某一處一直等待I2C主機(jī)的操作;而輪詢的方式很容易捕捉不到I2C的請(qǐng)求或者事件;所以如下硬件I2C從機(jī)通訊的方式使用的就是中斷處理方式,I2C主機(jī)任何操作和請(qǐng)求都會(huì)映射成對(duì)應(yīng)的中斷,待從機(jī)檢測(cè)到了之后,進(jìn)入中斷進(jìn)行相應(yīng)的處理,同時(shí)中斷的方式也保證了通訊的正常和穩(wěn)定性。
現(xiàn)在市面上很多MCU的I2C從機(jī)模式都支持多地址模式,但每家的IP功能設(shè)計(jì)都不一樣:有些是直接通過寄存器設(shè)置從機(jī)地址方式,這種方式限制了所支持從機(jī)地址的個(gè)數(shù);有些是通過地址掩碼的方式(類似于CAN通訊的ID濾波器),通過逐位比較的方式來判別所支持的I2C從機(jī)地址,這種方式可以支持很多個(gè)從機(jī)地址;第二種方式相比于第一種實(shí)現(xiàn)方式更靈活,支持的從機(jī)設(shè)備地址也更多!
MM32F032不支持多地址從機(jī)功能,但MM32F0140支持從機(jī)多地址通訊,可以根據(jù)實(shí)際項(xiàng)目需求選擇對(duì)應(yīng)的芯片型號(hào);從機(jī)多地址功能采用的是地址掩碼方式來過濾從機(jī)地址的,這樣可以支持更多的從機(jī)設(shè)備地址;通過設(shè)置從機(jī)設(shè)備地址和從機(jī)地址掩碼來實(shí)現(xiàn)從機(jī)多地址通訊功能;硬件I2C從機(jī)通訊具體的代碼實(shí)現(xiàn)如下:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
實(shí)測(cè)結(jié)果如下所示: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));
}
}
?
四、軟件模擬I2C從機(jī)通訊
軟件模擬I2C從機(jī)通訊是I2C通訊時(shí)序逆向的實(shí)現(xiàn)過程,它需要通過捕捉I2C主機(jī)的信號(hào)時(shí)序?qū)χ鳈C(jī)的事件、請(qǐng)求,以及發(fā)送過來的數(shù)據(jù)進(jìn)行解析,又要正確的回復(fù)I2C主機(jī),所以它的實(shí)現(xiàn)方式比I2C模擬主機(jī)完全不同。這需要開發(fā)者對(duì)I2C時(shí)序十分熟悉,所以在研讀下面軟件模擬I2C從機(jī)通訊程序時(shí),建議對(duì)照I2C時(shí)序一點(diǎn)點(diǎn)分析(提示:這部分內(nèi)容有點(diǎn)難度)。
對(duì)于軟件模擬I2C從機(jī)通訊的實(shí)現(xiàn)是通過兩個(gè)GPIO端口引腳分別與I2C主機(jī)的SCL和SDA進(jìn)行連接,程序中將這兩個(gè)GPIO端口引腳配置成外部中斷EXTI工作模式,通過捕獲GPIO端口引腳的上升沿、下降沿,以及高低電平狀態(tài),配合軟件模擬I2C從機(jī)的狀態(tài)管理,實(shí)現(xiàn)與I2C主機(jī)之間的通訊功能,在如下的程序中添加了詳細(xì)的注釋和說明,方便大家閱讀和理解,具體的代碼實(shí)現(xiàn)如下:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
實(shí)測(cè)結(jié)果如下所示: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端口, 默認(rèn)設(shè)置成輸入模式, 并使能相應(yīng)的外部觸發(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] 設(shè)置SDA信號(hào)線的輸入輸出方便, 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 設(shè)置SDA信號(hào)線的輸出電平(高電平 / 低電平)
* @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 當(dāng)SCL觸發(fā)上升沿外部中斷時(shí)的處理
* @param
* @retval
* @attention
******************************************************************************/
void sI2C_SLAVE_SCL_RiseHandler(sI2C_SLAVE_TypeDef *sI2C)
{
/* SCL為上升沿, 數(shù)據(jù)鎖定, 主從機(jī)從SDA總線上獲取數(shù)據(jù)位 */
switch(sI2C_SLAVE_State)
{
case sI2C_SLAVE_STATE_ADD:
/* I2C發(fā)送遵義MSB, 先發(fā)送高位, 再發(fā)送低位, 所以在接收的時(shí)候, 數(shù)據(jù)進(jìn)行左移 */
sI2C_SLAVE_SlaveAddress <<= 1;
sI2C_SLAVE_ShiftCounter += 1;
if(sI2C_SLAVE_READ_SDA(sI2C) == Bit_SET)
{
sI2C_SLAVE_SlaveAddress |= 0x01;
}
/* 當(dāng)接收到8位地址位后, 從機(jī)需要在第9個(gè)時(shí)鐘給出ACK應(yīng)答, 等待SCL下降沿的時(shí)候給出ACK信號(hào) */
if(sI2C_SLAVE_ShiftCounter == 8)
{
sI2C_SLAVE_State = sI2C_SLAVE_STATE_ADD_ACK;
}
break;
case sI2C_SLAVE_STATE_ADD_ACK:
/* 從機(jī)地址的ACK回復(fù)后, 切換到收發(fā)數(shù)據(jù)狀態(tài) */
sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT;
sI2C_SLAVE_ShiftCounter = 0; /* 數(shù)據(jù)移位計(jì)數(shù)器清零 */
sI2C_SLAVE_ReceivedData = 0; /* sI2C_SLAVE的接收數(shù)據(jù)清零 */
break;
case sI2C_SLAVE_STATE_DAT:
if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)
{
/* 主機(jī)寫操作:此時(shí)從機(jī)應(yīng)該獲取主機(jī)發(fā)送的SDA信號(hào)線電平狀態(tài), 進(jìn)行位存儲(chǔ) */
sI2C_SLAVE_ReceivedData <<= 1;
sI2C_SLAVE_ShiftCounter += 1;
if(sI2C_SLAVE_READ_SDA(sI2C) == Bit_SET)
{
sI2C_SLAVE_ReceivedData |= 0x01;
}
/* 當(dāng)收到一個(gè)完整的8位數(shù)據(jù)時(shí), 將收到的數(shù)據(jù)存放到I2C接收消息隊(duì)列中, 狀態(tài)轉(zhuǎn)換到給主機(jī)發(fā)送ACK應(yīng)答 */
if(sI2C_SLAVE_ShiftCounter == 8)
{
QUEUE_WRITE(QUEUE_SI2C_SLAVE_IDX, sI2C_SLAVE_ReceivedData);
sI2C_SLAVE_ShiftCounter = 0; /* 數(shù)據(jù)移位計(jì)數(shù)器清零 */
sI2C_SLAVE_ReceivedData = 0; /* sI2C_SLAVE的接收數(shù)據(jù)清零 */
sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT_ACK;
}
}
else
{
/* 主機(jī)讀操作:在SCL上升沿的時(shí)候, 主機(jī)獲取當(dāng)前SDA的狀態(tài)位, 如果到了第8個(gè)數(shù)位的上升沿,
* 那接下來就是主機(jī)回復(fù)從機(jī)的應(yīng)答或非應(yīng)答信號(hào)了, 所以將狀態(tài)切換到等待ACK的狀態(tài), 同時(shí)準(zhǔn)備下一個(gè)需要發(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)
{
/* 主機(jī)寫操作:從機(jī)發(fā)送ACK, 等待主機(jī)讀取從機(jī)發(fā)送的ACK信號(hào) */
sI2C_SLAVE_State = sI2C_SLAVE_STATE_DAT; /* 狀態(tài)切換到數(shù)據(jù)接收狀態(tài) */
}
else
{
/* 主機(jī)讀操作:主機(jī)發(fā)送ACK, 從機(jī)可以讀取主機(jī)發(fā)送的ACK信號(hào) */
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 當(dāng)SCL觸發(fā)下降沿外部中斷時(shí)的處理
* @param
* @retval
* @attention
******************************************************************************/
void sI2C_SLAVE_SCL_FallHandler(sI2C_SLAVE_TypeDef *sI2C)
{
/* SCL為下降沿, 數(shù)據(jù)可變 */
switch(sI2C_SLAVE_State)
{
case sI2C_SLAVE_STATE_STA:
/*
* 檢測(cè)到START信號(hào)后, SCL第一個(gè)下降沿表示開始傳輸Slave Address,
* 根據(jù)數(shù)據(jù)有效性的規(guī)則, 地址的第一位需要等到SCL變?yōu)楦唠娖綍r(shí)才可以讀取
* 切換到獲取Slave Address的狀態(tài), 等待SCL的上升沿觸發(fā)
*/
sI2C_SLAVE_State = sI2C_SLAVE_STATE_ADD;
sI2C_SLAVE_ShiftCounter = 0; /* 數(shù)據(jù)移位計(jì)數(shù)器清零 */
sI2C_SLAVE_SlaveAddress = 0; /* sI2C_SLAVE的從機(jī)地址清零 */
sI2C_SLAVE_ReceivedData = 0; /* sI2C_SLAVE的接收數(shù)據(jù)清零 */
break;
case sI2C_SLAVE_STATE_ADD:
/*
* 在主機(jī)發(fā)送Slave Address的時(shí)候, 從機(jī)只是讀取SDA狀態(tài), 進(jìn)行地址解析, 所以這邊沒有處理
*/
break;
case sI2C_SLAVE_STATE_ADD_ACK:
/* SCL低電平的時(shí)候, 給I2C總線發(fā)送地址的應(yīng)答信號(hào), 狀態(tài)不發(fā)生改變, 等待下一個(gè)上升沿將ACK發(fā)送出去 */
sI2C_SLAVE_SDA_SetLevel(sI2C, 0); /* 將SDA信號(hào)拉低, 向主機(jī)發(fā)送ACK信號(hào) */
break;
case sI2C_SLAVE_STATE_DAT:
/* 在SCL時(shí)鐘信號(hào)的下降沿, SDA信號(hào)線處理可變的狀態(tài) */
if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)
{
/* 主機(jī)寫操作:將SDA信號(hào)線設(shè)置成獲取狀態(tài), 等待下一個(gè)SCL上升沿時(shí)獲取數(shù)據(jù)位 */
sI2C_SLAVE_SDA_SetDirection(sI2C, 1);
}
else
{
/* 主機(jī)讀操作:根據(jù)發(fā)送的數(shù)據(jù)位設(shè)置SDA信號(hào)線的輸出電平, 等待下一個(gè)SCL上升沿時(shí)發(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個(gè)SCL時(shí)鐘信號(hào)下降沿的處理 */
if((sI2C_SLAVE_SlaveAddress & 0x01) == 0x00)
{
/* 主機(jī)寫操作:從機(jī)在接收到數(shù)據(jù)后, 需要給主機(jī)一個(gè)ACK應(yīng)答信號(hào), 狀態(tài)不發(fā)生改變, 等待下一個(gè)上升沿將ACK發(fā)送出去 */
sI2C_SLAVE_SDA_SetLevel(sI2C, 0); /* 將SDA信號(hào)拉低, 向主機(jī)發(fā)送ACK信號(hào) */
}
else
{
/* 主機(jī)讀操作:從機(jī)需要釋放當(dāng)前的SDA信號(hào)線, 以便主機(jī)發(fā)送ACK或NACK給從機(jī), 狀態(tài)不發(fā)生改變, 等待下一個(gè)上升沿讀取ACK信號(hào) */
sI2C_SLAVE_SDA_SetDirection(sI2C, 1);
}
break;
default:
break;
}
}
/**
* @brief 當(dāng)SDA觸發(fā)上升沿外部中斷時(shí)的處理
* @param None
* @retval None
*/
void sI2C_SLAVE_SDA_RiseHandler(sI2C_SLAVE_TypeDef *sI2C)
{
if(sI2C_SLAVE_READ_SCL(sI2C) == Bit_SET) /* SCL為高時(shí),SDA為上升沿:STOP */
{
sI2C_SLAVE_State = sI2C_SLAVE_STATE_STO;
}
else /* SCL為低時(shí),SDA為上升沿:數(shù)據(jù)的變化 */
{
}
}
/**
* @brief 當(dāng)SDA觸發(fā)下降沿外部中斷時(shí)的處理
* @param None
* @retval None
*/
void sI2C_SLAVE_SDA_FallHandler(sI2C_SLAVE_TypeDef *sI2C)
{
if(sI2C_SLAVE_READ_SCL(sI2C) == Bit_SET) /* SCL為高時(shí),SDA為下降沿:START */
{
sI2C_SLAVE_State = sI2C_SLAVE_STATE_STA;
}
else /* SCL為低時(shí),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);
}
}
以上就是基于MM32生態(tài)實(shí)現(xiàn)I2C接口通訊的幾種方式了,如果有需要查看原圖、軟件工程源代碼、I2C相關(guān)資料的小伙伴,請(qǐng)點(diǎn)擊底部“閱讀原文”進(jìn)行下載。
評(píng)論
查看更多