1. 程式人生 > >常用串行通信

常用串行通信

sof rom 時間 err top tap 兩個 寫入 信號

串行通信的速度較並行低,但是非常節省端口資源,所以是底層經常接觸到的通信方式。

一、串口通信

二、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通信

常用串行通信