常用串行通信
串行通信的速度較并行低,但是非常節(jié)省端口資源,所以是底層經(jīng)常接觸到的通信方式。
本文分為4部分
1、RS232串口通信
2、I2C通信
3、SPI通信
4、CAN通信
一、RS232串口通信
?
二、I2C通信
在I2C通信中,每個器件都有自己的固定地址,分為7位尋址和10位尋址模式,其中7位尋址較為常用,即每個設(shè)備的地址為7位。
通信速率:
標(biāo)準(zhǔn)模式:100kbit/s
快速模式:400kbit/s
高速模式:3.4Mbit/s
在通信中,首先發(fā)送起始條件 然后發(fā)送目標(biāo)器件的7位地址+R/W位,等待應(yīng)答,發(fā)送/接收數(shù)據(jù),等待應(yīng)答/作出應(yīng)答,發(fā)送結(jié)束條件。
另外,存在廣播呼叫方式:發(fā)送個地址0x00,對所有從設(shè)備進(jìn)行通信(不是所有器件都適用)
?
以MPU6050慣性傳感器為例,編寫模擬I2C的主機(jī)程序
在芯片手冊中找到描述如下
2.1編寫分段函數(shù)
II1_SDA_HGH-拉高數(shù)據(jù)線
II1_SDA_LOW-拉低數(shù)據(jù)線
II1_SCL_HIGH-拉高時鐘線
II1_SCL_LOW-拉低數(shù)據(jù)線
II1_SDA_DATA-讀取SDA線上的電平
IIC_Delay(x):定義一個x*4us的延時函數(shù)
2.1.1發(fā)起開始命令(Start condition)
I2C總線平常處于空閑狀態(tài),SDA和SCL均為高電平。發(fā)起開始命令的做法是,SDA從高到低跳變,I2C總線從空閑->忙
void IIC_WriteStartCondition(void)
{
IIC1_SDA_HIGH;
IIC1_SCK_HIGH; //如圖開始的時候兩個數(shù)據(jù)都處于高電平
IIC_Delay(2); //延時一段時間后
IIC1_SDA_LOW; //SDA先與SCL拉低,即為起始條件
IIC_Delay(2); ????//延時一段時間后
IIC1_SCK_LOW; //SCL拉低,并開始第一針數(shù)據(jù)(ADDRESS)的收發(fā),也是起始條件結(jié)束
IIC_Delay(1); //延時半個周期為下一個函數(shù)作準(zhǔn)備
}
?
2.1.2數(shù)據(jù)傳輸函數(shù)
數(shù)據(jù)傳輸中,SDA電平改變只能發(fā)生在SCL為低的期間,根據(jù)時序圖,可以知道在發(fā)送起始條件后需要發(fā)送從機(jī)地址和寫位,表示對總線上相應(yīng)的從機(jī)進(jìn)行寫操作:
void I2C_WriteByteDataToSlave(uint8_t data)
{
IIC1_SCK_LOW; //再次拉低數(shù)據(jù)線,可以忽略
for(i=0;i<8;i++) //發(fā)送一個字節(jié)(8位數(shù)據(jù))
{
(data & 0x80) ? IIC1_SDA_HIGH : IIC1_SDA_LOW;//判斷數(shù)據(jù)最高位是否為1,若是拉高數(shù)據(jù)線,否則拉低數(shù)據(jù)線,表示傳輸字節(jié)1/0
data <<= 1; 把數(shù)據(jù)左移1位,把剛剛發(fā)送完的數(shù)據(jù)剔除
IIC1_SCK_HIGH;//拉高時鐘線表示1個位傳輸結(jié)束
IIC_Delay(1); ??//延時半個周期
IIC1_SCK_LOW; //把SCL拉到低電平準(zhǔn)備發(fā)送下一位數(shù)據(jù)
IIC_Delay(1);
}
}
2.1.3 等待應(yīng)答
I2C發(fā)送第一幀數(shù)據(jù)完畢后,需要等待從機(jī)返回應(yīng)答以確保它收到了信息。等待應(yīng)答的時候主機(jī)釋放SDA線,從機(jī)接管SDA并保證其低電平直到下一個SCL高電平結(jié)束,編程上這里使第9個SCL信號拉高后,一直讀取SDA信號直到出現(xiàn)低電平才跳出,最后主機(jī)拉低SCL,拉高SDA
Uint8_t IIC_WaitSlaveAsck(void)
{
uint8_t tim = 0;
IIC1_SCK_HIGH; ??//拉高SCL線
while(IIC1_SDA_DATA) //當(dāng)數(shù)據(jù)線為高電平
{
tim++;
Delay(1);
if(tim > 50) //設(shè)計一個倒計時,超過50ms則認(rèn)為從機(jī)無應(yīng)答,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發(fā)起停止(Stop condition)
停止信號后釋放I2C總線,總線返回空閑狀態(tài),操作是當(dāng)SCL在高電平時,SDA發(fā)生從第到高的跳變
void IIC_StopCondition(void)
{
IIC1_SDA_LOW;//先讓SDA處于低電平
IIC1_SCK_HIGH;//拉高SCL線
IIC_Delay(2);//延時
IIC1_SDA_HIGH;//SDA發(fā)生從低到高的跳變
IIC_Delay(2);//延時一段時間等待通信結(jié)束
}
2.1.5數(shù)據(jù)接收
當(dāng)我們進(jìn)行MPU6050的讀取操作時,需要接收來自MPU6050的數(shù)據(jù),具體操作為如2.1.1發(fā)起Start ?然后發(fā)送地址及讀取位,之后產(chǎn)生SCL時鐘信號,并釋放SDA線由MPU6050接管,在SCL高電平接收數(shù)據(jù)
uint8_t IIC_ReadByteDataFromSlave(void)
{
uint8_t i,data=0;?
for(i=0;i<8;i++)
{
IIC1_SCK_HIGH; //拉高時鐘線
IIC_Delay(1); //延時一個周期
data <<= 1; //把數(shù)據(jù)左移一位
data |= IIC1_SDA_DATA; //把新來的數(shù)據(jù)加在位
IIC1_SCK_LOW; //拉低時鐘線
IIC_Delay(1); //延時
}
return data; //返回接收到的數(shù)據(jù)
}
2.1.6主機(jī)應(yīng)答
在接收到MPU6050的數(shù)據(jù)時,需要作出應(yīng)答(連讀模式時)來告訴MPU6050繼續(xù)發(fā)送下一幀數(shù)據(jù),或者不作出應(yīng)答直接發(fā)出Stop condition表示通信結(jié)束
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 單字節(jié)寫:
本段函數(shù)中分為8個部分,分別是:產(chǎn)生起始信號、寫入從機(jī)地址+寫位、等待應(yīng)答、寫入寄存器地址、等待應(yīng)答、寫入數(shù)據(jù)、等待應(yīng)答、產(chǎn)生停止信號。根據(jù)已寫成的函數(shù)進(jìn)行組合。
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爆發(fā)寫(連續(xù)寫)
關(guān)鍵在于發(fā)送寄存器地址以后可以一直只發(fā)送數(shù)據(jù)進(jìn)行寫入,利用for循環(huán)體。
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單字節(jié)讀
本函數(shù)分為11段:分別是 起始條件、從機(jī)地址加寫、等待應(yīng)答、寄存器地址、等待從機(jī)應(yīng)答、再次發(fā)送起始條件、從機(jī)地址加讀、等待應(yīng)答、讀取數(shù)據(jù)、不應(yīng)答,停止條件
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爆發(fā)式讀(連讀)
根據(jù)單字節(jié)讀稍作修改
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通信
3.1 SPI重要參數(shù)
SPI為4線的串行通信協(xié)議
分別為
1、SCK:發(fā)生時鐘信號,由主機(jī)發(fā)出信號
2、MISO:主進(jìn)從出,主機(jī)的接收信號端,由從機(jī)發(fā)出信號
3、MOSI:主出從進(jìn),主機(jī)的發(fā)送信號端,由主機(jī)發(fā)出信號
4、CS:片選,主機(jī)控制(拉低表示傳輸開始)
也有部分設(shè)備省略CS端,稱為3線SPI
首先SPI的兩個參數(shù)會導(dǎo)致程序編寫的不同
1 時鐘極性(CPOL)
2 時鐘相位(CPHA)
當(dāng)CPOL=0的時候,串行同步時鐘的空閑狀態(tài)為低電平
當(dāng)CPOL=1的時候,串行同步時鐘的空閑狀態(tài)為高電平
如下圖所示:紅框內(nèi)表示空閑時間電平
當(dāng)CPHA=0,串行同步時鐘的第一個跳變沿(上升下降沿)數(shù)據(jù)被采樣
當(dāng)CPHA=1,串行同步時鐘的第二個跳變沿(上升下降沿)數(shù)據(jù)被采樣
如下圖所示:紅框內(nèi)表示1還是2跳變沿采樣
3.2模擬SPI串行通信
設(shè)定CPOL=0;CPHA=0;數(shù)據(jù)從MSB開始發(fā)送,如下圖所示
3.2.1 主機(jī)部分
注釋:以下程序出現(xiàn)到的函數(shù)
Uint8_t:定義為unsigned char 類型
CS_HIGH():表示拉高CS信號
CS_LOW():表示拉低CS信號
SCK_HIGH():表示拉高時鐘線
SCK_LOW():表示拉低時鐘線
MOSI_HIGH():表示拉高MOSI信號
MOSI_LOW():表示拉低MOSI信號
READ_MISO():表示讀取MISO信號
SPI_DELAY(x):表示延時x*1/2個時鐘周期
3.2.1.1 編寫讀一字節(jié)的函數(shù)
uint8_t SPI_ReadWriteByte(uint8_t Data)
{
uint8_t i;
uint8_t ReturnData;
?
CS_LOW();//拉低片選信號準(zhǔn)備開始數(shù)據(jù)傳輸
SPI_DELAY(2);//等待從機(jī)反應(yīng)過來
for(i=0;i<8;i++)
{
If(Data&0x80==0x80)
MOSI_HIGH();//如果最高位為高電平則
else
MOSI_LOW();//如果最高位為低電平則拉低MOSI線
Data=Data<<1; ??//把數(shù)據(jù)左移一位準(zhǔn)備發(fā)送下一位數(shù)據(jù)
SPI_DELAY(1):
SCK_HIGH(): ???//在上升沿數(shù)據(jù)生效
ReturnData<<1;
ReturnData|=READ_MISO()://把新接收到的數(shù)據(jù)作為次高位
SPI_DELAY(1):
SCL_LOW(); ???//拉低時鐘信號
}
SPI_DELAY(2);//延時一段時間等待從機(jī)完全接收完成
CS_HIGH();//拉高片選信號表示傳輸結(jié)束
return ReturnData;//返回接收到的數(shù)據(jù)
}
3.2.1 從機(jī)部分
MISO_HIGH():表示拉高MISO信號
MISO_LOW():表示拉低MISO信號
READ_MOSI():表示讀取MOSI信號
READ_SCK():表示讀取SCK信號
?
3.2.2.1 編寫讀一字節(jié)的函數(shù)
uint8_t SPI_ReadWriteByte(uint8_t Data)
{
uint8_t i;
uint8_t ReturnData;
?
for(i=0;i<8;i++)
{
If(data&0x80==0x80)
MISO_HIGH():
else
MISO_LOW():
Data<<1;
While(!READ_SCK);//等待上升沿
ReturnData<<1;
ReturnData|=READ_MISO()://把新接收到的數(shù)據(jù)作為次高位
While(READ_SCK);//等待下一次低電平的到來
}
Return ReturnData;
}
?
四、CAN通信
4.1CAN總線簡介
在這里介紹四種通信中最特別的通信方式,常常用于汽車電子中,其特色只需要鏈接2線CAN_LOW和CAN_HIGH(不用接地線),以及是一種多主的通信方式,具有抗干擾能力強(qiáng)、通信速度高(常用125K、500K)、通信距離遠(yuǎn)、自動診斷剔除錯誤設(shè)備等等的優(yōu)點。是多機(jī)組網(wǎng)通信的優(yōu)秀形式。
它也存在缺點:每幀攜帶的數(shù)據(jù)較少(最多8個字節(jié))、需要外掛設(shè)備
4.2CAN總線電平
CAN通信使用的兩根線(常以雙絞線形式出現(xiàn))CAN_LOW和CAN_H
(此處補(bǔ)圖)
顯性電平:邏輯0:CAN_LOW輸出1.5V ?CAN_HIGH輸出3.5V,兩線電壓差2V
隱性電平:邏輯1:CAN_HIGH輸出2.5V CAN_HIGH輸出2.5V ,兩線電壓差0V
電氣連接:
上拉模式:同I2C的連接方式,CAN_L 和CAN_H接上拉電阻到電源
回環(huán)模式:在CAN_L和CAN_H之間連接電阻
(此處補(bǔ)圖)
4.4CAN總線數(shù)據(jù)幀
CAN總線協(xié)議具有固定報文形式,以下對其進(jìn)行詳細(xì)介紹
首先是4種基本幀:
一、標(biāo)準(zhǔn)數(shù)據(jù)幀
(此處補(bǔ)圖)
組成:1位SOF+11位仲裁段(標(biāo)準(zhǔn)ID+RTR)+6位控制段(IDE+r0+DLC)+ 0~64位數(shù)據(jù)段(MSB在前)+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
中裁段:
ID段:ID號越小,表示具有越高的優(yōu)先級,當(dāng)CAN總線同時有兩個主機(jī)發(fā)送信息時,優(yōu)先級高的信息具有有限傳送資格
RTR:遠(yuǎn)程發(fā)送請求位:標(biāo)準(zhǔn)幀中為顯性
控制段:
IDE:識別符擴(kuò)展位:在標(biāo)準(zhǔn)幀中為控制段,并為顯性。
R0:保留
DLC:
二、擴(kuò)展數(shù)據(jù)幀
(此處補(bǔ)圖)
組成:SOF+29位仲裁段(標(biāo)準(zhǔn)ID+RRR+IDE+擴(kuò)展ID+RTR)+6位控制段(r0+r1+DLC)+ 0~64位數(shù)據(jù)段(MSB在前)+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
三、標(biāo)準(zhǔn)遠(yuǎn)程幀(標(biāo)準(zhǔn)遙控幀)
(此處補(bǔ)圖)
組成同標(biāo)準(zhǔn)數(shù)據(jù)幀,但是沒有數(shù)據(jù)段
組成:1位SOF+11位仲裁段(標(biāo)準(zhǔn)ID+RTR)+6位控制段(IDE+r0+DLC)+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
四、擴(kuò)展遠(yuǎn)程幀(擴(kuò)展遙控幀)
(此處補(bǔ)圖)
組成同擴(kuò)展數(shù)據(jù)幀,但是沒有數(shù)據(jù)段
組成:SOF+29位仲裁段(標(biāo)準(zhǔn)ID+RRR+IDE+擴(kuò)展ID+RTR)+6位控制段(r0+r1+DLC))+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
?
轉(zhuǎn)載于:https://www.cnblogs.com/HongYi-Liang/p/7058587.html
總結(jié)
- 上一篇: 简单的小程序实现ATM机操作
- 下一篇: JAVA课程设计——“小羊吃蓝莓”小游戏