常用串行通信
串行通信的速度較並行低,但是非常節省端口資源,所以是底層經常接觸到的通信方式。
一、串口通信
二、I2C通信
以MPU6050慣性傳感器為例,編寫模擬I2C的主機程序
2.1編寫分段函數
2.1.1發起開始命令(Start condition)
I2C總線平常處於空閑狀態,SDA和SCL均為高電平。發起開始命令的做法是,SDA從高到低跳變,I2C總線從空閑->忙
void IIC_WriteStartCondition(void)
{
IIC1_SDA_HIGH;
IIC1_SCK_HIGH; //如圖開始的時候兩個數據都處於高電平
IIC_Delay(2); //延時一段時間後
IIC1_SDA_LOW; //SDA先與SCL拉低,即為起始條件
IIC_Delay(2); //延時一段時間後
IIC1_SCK_LOW; //SCL拉低,並開始第一針數據(ADDRESS)的收發,也是起始條件結束
IIC_Delay(1); //延時半個周期為下一個函數作準備
}
2.1.2數據傳輸函數
數據傳輸中,SDA電平改變只能發生在SCL為低的期間,根據時序圖,可以知道在發送起始條件後需要發送從機地址和寫位,表示對總線上相應的從機進行寫操作:
void I2C_WriteByteDataToSlave(uint8_t data)
{
IIC1_SCK_LOW;
for(i=0;i<8;i++) //發送一個字節(8位數據)
{
(data & 0x80) ? IIC1_SDA_HIGH : IIC1_SDA_LOW;//判斷數據最高位是否為1,若是拉高數據線,否則拉低數據線,表示傳輸字節1/0
data <<= 1; 把數據左移1位,把剛剛發送完的數據剔除
IIC1_SCK_HIGH;//拉高時鐘線表示1個位傳輸結束
IIC_Delay(1); //延時半個周期
IIC1_SCK_LOW; //把SCL拉到低電平準備發送下一位數據
IIC_Delay(1);
}
}
2.1.3 等待應答
I2C發送第一幀數據完畢後,需要等待從機返回應答以確保它收到了信息。等待應答的時候主機釋放SDA線,從機接管SDA並保證其低電平直到下一個SCL高電平結束,編程上這裏使第9個SCL信號拉高後,一直讀取SDA信號直到出現低電平才跳出,最後主機拉低SCL,拉高SDA
Uint8_t IIC_WaitSlaveAsck(void)
{
uint8_t tim = 0;
IIC1_SCK_HIGH; //拉高SCL線
while(IIC1_SDA_DATA) //當數據線為高電平
{
tim++;
Delay(1);
if(tim > 50) //設計一個倒計時,超過50ms則認為從機無應答,I2c出錯,返回1
{
tim=0;
I2CERR++;
return 1;
}
}
IIC_Delay(1);
IIC1_SCK_LOW;
IIC1_SDA_HIGH;
IIC_Delay(1);
return 0; //返回0
}
2.1.4發起停止(Stop condition)
停止信號後釋放I2C總線,總線返回空閑狀態,操作是當SCL在高電平時,SDA發生從第到高的跳變
void IIC_StopCondition(void)
{
IIC1_SDA_LOW;//先讓SDA處於低電平
IIC1_SCK_HIGH;//拉高SCL線
IIC_Delay(2);//延時
IIC1_SDA_HIGH;//SDA發生從低到高的跳變
IIC_Delay(2);//延時一段時間等待通信結束
}
2.1.5數據接收
當我們進行MPU6050的讀取操作時,需要接收來自MPU6050的數據,具體操作為如2.1.1發起Start 然後發送地址及讀取位,之後產生SCL時鐘信號,並釋放SDA線由MPU6050接管,在SCL高電平接收數據
uint8_t IIC_ReadByteDataFromSlave(void)
{
uint8_t i,data=0;
for(i=0;i<8;i++)
{
IIC1_SCK_HIGH; //拉高時鐘線
IIC_Delay(1); //延時一個周期
data <<= 1; //把數據左移一位
data |= IIC1_SDA_DATA; //把新來的數據加在位
IIC1_SCK_LOW; //拉低時鐘線
IIC_Delay(1); //延時
}
return data; //返回接收到的數據
}
2.1.6主機應答
在接收到MPU6050的數據時,需要作出應答(連讀模式時)來告訴MPU6050繼續發送下一幀數據,或者不作出應答直接發出Stop condition表示通信結束
void IIC_MastAsckToSlave(void)
{
IIC1_SCK_LOW;
IIC1_SDA_LOW;
// IIC_Delay(1);
IIC1_SCK_HIGH;
IIC_Delay(1);
IIC1_SCK_LOW;
IIC1_SDA_HIGH;
IIC_Delay(1);
}
void IIC_MastNoteAsckToSlave(void)
{
IIC1_SCK_LOW;
IIC1_SDA_HIGH;
// IIC_Delay(1);
IIC1_SCK_HIGH;
IIC_Delay(1);
IIC1_SCK_LOW;
IIC_Delay(1);
}
2.2編寫整段讀取
2.2.1 單字節寫:
本段函數中分為8個部分,分別是:產生起始信號、寫入從機地址+寫位、等待應答、寫入寄存器地址、等待應答、寫入數據、等待應答、產生停止信號。根據已寫成的函數進行組合
void MPU6050_SingleByteWrite_Soft( I2C_TypeDef* I2Cx, uint8_t RegisterAddress, uint8_t Data )
{
IIC_WriteStartCondition(); //1
IIC_WriteByteDataToSlave(MPU6050_I2C_ADDRESS);//2
IIC_WaitSlaveAsck(); //3
IIC_WriteByteDataToSlave(RegisterAddress); //4
IIC_WaitSlaveAsck(); //5
IIC_WriteByteDataToSlave(Data); //6
IIC_WaitSlaveAsck(); //7
IIC_StopCondition(); //8
}
2.2.2爆發寫(連續寫)
關鍵在於發送寄存器地址以後可以一直只發送數據進行寫入,所以利用循環體。
void MPU6050_BurstWrite_Soft( I2C_TypeDef* I2Cx, uint8_t RegisterAddress, uint8_t* DataPointer, uint8_t DataLength )
{
u8 i;
IIC_WriteStartCondition();
IIC_WriteByteDataToSlave(MPU6050_I2C_ADDRESS);
IIC_WaitSlaveAsck();
IIC_WriteByteDataToSlave(RegisterAddress);
IIC_WaitSlaveAsck();
for(i=0;i<DataLength;i++)
{
IIC_WriteByteDataToSlave(*(DataPointer+i));
IIC_WaitSlaveAsck();
}
IIC_StopCondition();
}
2.2.3單字節寫
本函數分為11段:分別是 起始條件、從機地址加寫、等待應答、寄存器地址、等待從機應答、再次發送起始條件、從機地址加讀、等待應答、讀取數據、不應答,停止條件
uint8_t MPU6050_SingleByteRead_Soft( I2C_TypeDef* I2Cx, uint8_t RegisterAddress )
{
uint8_t Data;
IIC_WriteStartCondition();
IIC_WriteByteDataToSlave(MPU6050_I2C_ADDRESS);
IIC_WaitSlaveAsck();
IIC_WriteByteDataToSlave(RegisterAddress);
IIC_WaitSlaveAsck();
IIC_WriteStartCondition();
IIC_WriteByteDataToSlave(MPU6050_I2C_ADDRESS+1);
IIC_WaitSlaveAsck();
Data = IIC_ReadByteDataFromSlave();
MastNoteAsckToSlave();
IIC_StopCondition();
return Data;
}
2.2.4爆發式讀(連讀)
同理爆發寫
void MPU6050_BurstRead_Soft( I2C_TypeDef* I2Cx, uint8_t RegisterAddress, uint8_t* DataPointer, uint8_t DataLength )
{
uint8_t i;
IIC_WriteStartCondition();
IIC_WriteByteDataToSlave(MPU6050_I2C_ADDRESS);
IIC_WaitSlaveAsck();
IIC_WriteByteDataToSlave(RegisterAddress);
IIC_WaitSlaveAsck();
IIC_WriteStartCondition();
IIC_WriteByteDataToSlave(MPU6050_I2C_ADDRESS+1);
IIC_WaitSlaveAsck();
for(i=0;i<DataLength-1;i++)
{
* (DataPointer+i)=IIC_ReadByteDataFromSlave();
MastAsckToSlave();
}
* (DataPointer+i)=IIC_ReadByteDataFromSlave();
MastNoteAsckToSlave();
IIC_StopCondition();
}
三、SPI通信
常用串行通信