常用串行通信
串行通信的速度較并行低,但是非常節省端口資源,所以是底層經常接觸到的通信方式。
本文分為4部分
1、RS232串口通信
2、I2C通信
3、SPI通信
4、CAN通信
一、RS232串口通信
?
二、I2C通信
在I2C通信中,每個器件都有自己的固定地址,分為7位尋址和10位尋址模式,其中7位尋址較為常用,即每個設備的地址為7位。
通信速率:
標準模式:100kbit/s
快速模式:400kbit/s
高速模式:3.4Mbit/s
在通信中,首先發送起始條件 然后發送目標器件的7位地址+R/W位,等待應答,發送/接收數據,等待應答/作出應答,發送結束條件。
另外,存在廣播呼叫方式:發送個地址0x00,對所有從設備進行通信(不是所有器件都適用)
?
以MPU6050慣性傳感器為例,編寫模擬I2C的主機程序
在芯片手冊中找到描述如下
2.1編寫分段函數
II1_SDA_HGH-拉高數據線
II1_SDA_LOW-拉低數據線
II1_SCL_HIGH-拉高時鐘線
II1_SCL_LOW-拉低數據線
II1_SDA_DATA-讀取SDA線上的電平
IIC_Delay(x):定義一個x*4us的延時函數
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爆發寫(連續寫)
關鍵在于發送寄存器地址以后可以一直只發送數據進行寫入,利用for循環體。
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通信
3.1 SPI重要參數
SPI為4線的串行通信協議
分別為
1、SCK:發生時鐘信號,由主機發出信號
2、MISO:主進從出,主機的接收信號端,由從機發出信號
3、MOSI:主出從進,主機的發送信號端,由主機發出信號
4、CS:片選,主機控制(拉低表示傳輸開始)
也有部分設備省略CS端,稱為3線SPI
首先SPI的兩個參數會導致程序編寫的不同
1 時鐘極性(CPOL)
2 時鐘相位(CPHA)
當CPOL=0的時候,串行同步時鐘的空閑狀態為低電平
當CPOL=1的時候,串行同步時鐘的空閑狀態為高電平
如下圖所示:紅框內表示空閑時間電平
當CPHA=0,串行同步時鐘的第一個跳變沿(上升下降沿)數據被采樣
當CPHA=1,串行同步時鐘的第二個跳變沿(上升下降沿)數據被采樣
如下圖所示:紅框內表示1還是2跳變沿采樣
3.2模擬SPI串行通信
設定CPOL=0;CPHA=0;數據從MSB開始發送,如下圖所示
3.2.1 主機部分
注釋:以下程序出現到的函數
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 編寫讀一字節的函數
uint8_t SPI_ReadWriteByte(uint8_t Data)
{
uint8_t i;
uint8_t ReturnData;
?
CS_LOW();//拉低片選信號準備開始數據傳輸
SPI_DELAY(2);//等待從機反應過來
for(i=0;i<8;i++)
{
If(Data&0x80==0x80)
MOSI_HIGH();//如果最高位為高電平則
else
MOSI_LOW();//如果最高位為低電平則拉低MOSI線
Data=Data<<1; ??//把數據左移一位準備發送下一位數據
SPI_DELAY(1):
SCK_HIGH(): ???//在上升沿數據生效
ReturnData<<1;
ReturnData|=READ_MISO()://把新接收到的數據作為次高位
SPI_DELAY(1):
SCL_LOW(); ???//拉低時鐘信號
}
SPI_DELAY(2);//延時一段時間等待從機完全接收完成
CS_HIGH();//拉高片選信號表示傳輸結束
return ReturnData;//返回接收到的數據
}
3.2.1 從機部分
MISO_HIGH():表示拉高MISO信號
MISO_LOW():表示拉低MISO信號
READ_MOSI():表示讀取MOSI信號
READ_SCK():表示讀取SCK信號
?
3.2.2.1 編寫讀一字節的函數
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()://把新接收到的數據作為次高位
While(READ_SCK);//等待下一次低電平的到來
}
Return ReturnData;
}
?
四、CAN通信
4.1CAN總線簡介
在這里介紹四種通信中最特別的通信方式,常常用于汽車電子中,其特色只需要鏈接2線CAN_LOW和CAN_HIGH(不用接地線),以及是一種多主的通信方式,具有抗干擾能力強、通信速度高(常用125K、500K)、通信距離遠、自動診斷剔除錯誤設備等等的優點。是多機組網通信的優秀形式。
它也存在缺點:每幀攜帶的數據較少(最多8個字節)、需要外掛設備
4.2CAN總線電平
CAN通信使用的兩根線(常以雙絞線形式出現)CAN_LOW和CAN_H
(此處補圖)
顯性電平:邏輯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接上拉電阻到電源
回環模式:在CAN_L和CAN_H之間連接電阻
(此處補圖)
4.4CAN總線數據幀
CAN總線協議具有固定報文形式,以下對其進行詳細介紹
首先是4種基本幀:
一、標準數據幀
(此處補圖)
組成:1位SOF+11位仲裁段(標準ID+RTR)+6位控制段(IDE+r0+DLC)+ 0~64位數據段(MSB在前)+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
中裁段:
ID段:ID號越小,表示具有越高的優先級,當CAN總線同時有兩個主機發送信息時,優先級高的信息具有有限傳送資格
RTR:遠程發送請求位:標準幀中為顯性
控制段:
IDE:識別符擴展位:在標準幀中為控制段,并為顯性。
R0:保留
DLC:
二、擴展數據幀
(此處補圖)
組成:SOF+29位仲裁段(標準ID+RRR+IDE+擴展ID+RTR)+6位控制段(r0+r1+DLC)+ 0~64位數據段(MSB在前)+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
三、標準遠程幀(標準遙控幀)
(此處補圖)
組成同標準數據幀,但是沒有數據段
組成:1位SOF+11位仲裁段(標準ID+RTR)+6位控制段(IDE+r0+DLC)+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
四、擴展遠程幀(擴展遙控幀)
(此處補圖)
組成同擴展數據幀,但是沒有數據段
組成:SOF+29位仲裁段(標準ID+RRR+IDE+擴展ID+RTR)+6位控制段(r0+r1+DLC))+15位CRC校驗段+ACK段(ACK槽+ACK界定符)+EOF
?
轉載于:https://www.cnblogs.com/HongYi-Liang/p/7058587.html
總結
- 上一篇: 简单的小程序实现ATM机操作
- 下一篇: JAVA课程设计——“小羊吃蓝莓”小游戏