I2C軟件模擬(記錄)
//MYI2C.C
#include "myi2c.h"
/*
1:將我們定義的SCL和SDA對應的引腳初始化為開漏輸出模式
2:根據(jù)I2C協(xié)議規(guī)定需將SCL和SDA置高電平
*/
void MyI2C_Init(void)
{
?? ?GPIO_InitTypeDef GPIO_InitStructurt;
?? ?GPIO_InitStructurt.GPIO_Mode = GPIO_Mode_Out_OD;//輸入時先置1為高阻態(tài),然后即可讀取輸入數(shù)據(jù)寄存器即可獲取輸入的數(shù)據(jù)
?? ?GPIO_InitStructurt.GPIO_Pin = SCL_PIN | SDA_PIN;
?? ?GPIO_InitStructurt.GPIO_Speed = GPIO_Speed_50MHz;
?? ?GPIO_Init(I2C_PORT,&GPIO_InitStructurt);
?? ?
?? ?GPIO_SetBits(I2C_PORT,SCL_PIN | SDA_PIN);
}
//寫SCL引腳
void MyI2C_W_SCL(uint8_t BitValue)
{
?? ?GPIO_WriteBit(I2C_PORT,SCL_PIN,(BitAction)BitValue);//使用宏定義引腳,
?? ?Delay_us(10);
}
//寫SDA引腳
void MyI2C_W_SDA(uint8_t BitValue)
{
?? ?GPIO_WriteBit(I2C_PORT,SDA_PIN,(BitAction)BitValue);
?? ?Delay_us(10);
}
//讀SDA引腳
uint8_t MyI2C_R_SDA(void)
{
?? ?uint8_t BitValue;//將讀取的數(shù)據(jù)通過該變量返回
?? ?BitValue = GPIO_ReadInputDataBit(I2C_PORT,SDA_PIN);//獲取該引腳此時處于什么電平
?? ?Delay_us(10);
?? ?return BitValue;
}
//起始條件時序
void MyI2C_Start(void)
{
?? ?//先釋放SDA,SCL(輸出高電平)
?? ?MyI2C_W_SDA(1);//先放SDA在前有助于該函數(shù)兼容起始條件和重復起始條件
?? ?MyI2C_W_SCL(1);
?? ?//起始條件時序
?? ?MyI2C_W_SDA(0);
?? ?MyI2C_W_SCL(0);
}
//停止條件時序
void MyI2C_Stop(void)
{
?? ?//確保SDA能產生上升沿,需先拉低SDA
?? ?MyI2C_W_SDA(0);
?? ?//SDA,SCL回歸默認高電平
?? ?MyI2C_W_SCL(1);
?? ?MyI2C_W_SDA(1);
}
//發(fā)送一個字節(jié)Byte。除終止條件時SCL以高電平結束外,
//其它所有的時序單元結束都應該保證以低電平結束,
//作用是方便各個時序單元的拼接。每放完一位后,應
//應先釋放SCL,再拉低SCL
void MyI2C_SendByte(uint8_t Byte)
{
?? ?uint8_t i;
?? ?for(i=0;i<8;i++)
?? ?{
?? ??? ?MyI2C_W_SDA(Byte & (0x80 >>i));//取BYTE的每一位,讀函數(shù)會判斷輸入的每一位,有1則1,無1則0
?? ??? ?MyI2C_W_SCL(1);//驅動時鐘走一個脈沖,讓從機讀剛寫入的位
?? ??? ?MyI2C_W_SCL(0);
?? ?}
}
//接收一個字節(jié)時序。SCL低電平,主機釋放SDA,從機把數(shù)據(jù)
//放到SDA上,主機釋放SCL,SCL變?yōu)楦唠娖?,主機開始接收數(shù)據(jù)
uint8_t MyI2C_ReceiveByte(void)
{
?? ?uint8_t i,Byte = 0x00;//接收到的數(shù)據(jù)
?? ?MyI2C_W_SDA(1);//釋放SDA
?? ?for(i=0;i<8;i++)
?? ?{
?? ??? ?MyI2C_W_SCL(1);//釋放SCL,讓從機先別發(fā)數(shù)據(jù),讀數(shù)據(jù)
?? ??? ?if(MyI2C_R_SDA()==1)
?? ??? ?{
?? ??? ??? ?Byte |= (0x80>>i);
?? ??? ?}
?? ??? ?MyI2C_W_SCL(0);//讓從機放數(shù)據(jù)到SDA上
?? ?}
?? ?return Byte;
}
//發(fā)送應答,進函數(shù)時,SCL為低電平
void MyI2C_SendACK(uint8_t ACKbit)
{
?? ?MyI2C_W_SDA(ACKbit);
?? ?MyI2C_W_SCL(1);//從機讀
?? ?MyI2C_W_SCL(0);
}
//接收應答,進函數(shù)時,SCL為低電平
uint8_t MyI2C_ReceiveACK(void)
{
?? ?uint8_t ACKbit;
?? ?MyI2C_W_SDA(1);//釋放SDA
?? ?MyI2C_W_SCL(1);//主機讀
?? ?ACKbit = MyI2C_R_SDA();//ACKbit=0為有從機應答
?? ?MyI2C_W_SCL(0);//進入下一個時序
?? ?return ACKbit;
}
//MYI2C.H
#ifndef __MYI2C_H
#define __MYI2C_H
#include "Device/Include/stm32f10x.h"?? // Device header
#include "Delay.h"
//使用宏定義方便后續(xù)使用其它引腳模擬時方便代碼的修改
#define I2C_PORT GPIOB
#define SCL_PIN GPIO_Pin_10
#define SDA_PIN GPIO_Pin_11
void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendACK(uint8_t ACKbit);
uint8_t MyI2C_ReceiveACK(void);
#endif
//以上代碼都是跟著江協(xié)大老師寫的,我只是加了點不知其所以然的注釋,發(fā)上這里來是為了以后好找回。