STM32F103學習筆記(1)——FreeRTOS下模擬I2C

一、硬件連接

功能口 引腳
SCL PB.6
SDA PB.5

二、移植文件

鏈接:https://pan.baidu.com/s/1wxbQTMlnX2pavrbW2RYg4g 提取碼:dxex
board_i2c.cboard_i2c.h 兩個文件加入工程的User文件夾下

注意:延時函數使用了FreeRTOS的vTaskDelay任務延時函數

2.1 board_i2c.c

/*********************************************************************
 * INCLUDES
 */
#include "FreeRTOS.h"
#include "task.h" 
 
#include "board_i2c.h"
#include "board_systick.h"

static void SDA_OUT_MODE(void);
static void SDA_IN_MODE(void);

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief I2C驅動初始化,採用模擬IO的方式實現
 @param 無
 @return 無
*/
void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(IIC_SCL_CLK | IIC_SDA_CLK, ENABLE);
	
    GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;            
    GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);
	
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
	
    IIC_Stop();             // 給一個停止信號, 復位I2C總線上的所有設備到待機模式
}

/**
 @brief CPU發起I2C總線啓動信號
 @param 無
 @return 無
*/
void IIC_Start(void)
{
    SDA_OUT_MODE();         // SDA線輸出模式
    
    IIC_SDA_1();            	  	  
    IIC_SCL_1();
    vTaskDelay(1);
    IIC_SDA_0();            // 當SCL高電平時,SDA出現一個下跳沿表示I2C總線啓動信號
    vTaskDelay(1);    
    IIC_SCL_0();            // 鉗住I2C總線,準備發送或接收數據 
}	

/**
 @brief CPU發起I2C總線停止信號
 @param 無
 @return 無
*/
void IIC_Stop(void)
{
    SDA_OUT_MODE();         // SDA線輸出模式
    
    IIC_SCL_0();
    IIC_SDA_0();          
    IIC_SCL_1();
    vTaskDelay(1);
    IIC_SDA_1();            // 當SCL高電平時,SDA出現一個上跳沿表示I2C總線停止信號		
    vTaskDelay(1);
}

/**
 @brief CPU向I2C總線設備發送8bit數據
 @param ucByte -[in] 等待發送的字節
 @return 無
*/  
void IIC_SendByte(uint8_t ucByte)
{                        
    uint8_t i;  
    
    SDA_OUT_MODE();         // SDA線輸出模式
    
    IIC_SCL_0();            // 拉低時鐘開始數據傳輸
    
    for(i = 0; i < 8; i++)
    {              
        if(ucByte & 0x80) 	
        {            
            IIC_SDA_1();
        }
        else
        {
            IIC_SDA_0();
        }
		ucByte <<= 1; 	
        vTaskDelay(1);        
        IIC_SCL_1();
        vTaskDelay(1);
        IIC_SCL_0();
        vTaskDelay(1);        
    }	 
} 

/**
 @brief CPU從I2C總線設備讀取8bit數據
 @param 無
 @return 讀到的數據
*/ 
uint8_t IIC_ReadByte(void)
{
    uint8_t i = 0;
    uint8_t value = 0;
    
    SDA_IN_MODE();          // SDA線輸入模式
    
    for(i = 0; i < 8; i++)
    {
        value <<= 1;
        IIC_SCL_1();
        vTaskDelay(1);
        if(IIC_SDA_READ())
        {
            value++;
        }            
        IIC_SCL_0(); 
        vTaskDelay(1); 
    }					
    IIC_Ack();
    
    return value;
}

/**
 @brief CPU產生一個時鐘,並讀取器件的ACK應答信號
 @param 無
 @return 返回0表示正確應答,1表示無器件響應
*/
uint8_t IIC_WaitAck(void)
{
    uint8_t result = 0; 
    
    SDA_IN_MODE();          // SDA線輸入模式
    
    IIC_SDA_1();            // CPU釋放SDA總線
    vTaskDelay(1); 
    IIC_SCL_1();            // CPU驅動SCL = 1, 此時器件會返回ACK應答
    vTaskDelay(1);
    if(IIC_SDA_READ())
    {
        result = 1;
    }
    else
    {
        result = 0;
    }
    IIC_SCL_0();
    vTaskDelay(1);

    return result;  
} 

/**
 @brief CPU產生一個ACK信號
 @param 無
 @return 無
*/
void IIC_Ack(void)
{
    SDA_OUT_MODE();         // SDA線輸出模式
    
    IIC_SDA_0();            // CPU驅動SDA = 0
    vTaskDelay(1);
    IIC_SCL_1();            // CPU產生1個時鐘
    vTaskDelay(1);
    IIC_SCL_0();
    vTaskDelay(1);
    IIC_SDA_1();            // CPU釋放SDA總線
}

/**
 @brief CPU產生1個NACK信號
 @param 無
 @return 無
*/    
void IIC_NAck(void)
{
    SDA_OUT_MODE();         // SDA線輸出模式
    
    IIC_SDA_1();            // CPU驅動SDA = 1
    vTaskDelay(1);
    IIC_SCL_1();            // CPU產生1個時鐘
    vTaskDelay(1);
    IIC_SCL_0();
    vTaskDelay(1);
}

/**
 @brief 檢測I2C總線設備,CPU向發送設備地址,然後讀取設備應答來判斷該設備是否存在
 @param address -[in] 設備的I2C總線地址+讀寫控制bit(0 = w, 1 = r)
 @return 0 - 表示正確, 1 - 表示未探測到
*/  
uint8_t IIC_CheckDevice(uint8_t address)
{
    uint8_t ucAck;

    IIC_Init();             // 初始化I2C
    IIC_Start();            // 發送啓動信號
    IIC_SendByte(address);  // 設備的I2C總線地址+讀寫控制bit(0 = w, 1 = r)
    ucAck = IIC_WaitAck();	// 檢測設備的ACK應答
    IIC_Stop();             // 發送停止信號

    return ucAck;
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief SDA輸出配置	
 @param 無
 @return 無
*/ 
static void SDA_OUT_MODE(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}

/**
 @brief SDA輸入配置	
 @param 無
 @return 無
*/ 
static void SDA_IN_MODE(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}

/****************************************************END OF FILE****************************************************/

2.2 board_i2c.h

#ifndef _BOARD_I2C_H_
#define _BOARD_I2C_H_

/*********************************************************************
 * INCLUDES
 */
#include "stm32f10x.h"

/*********************************************************************
 * DEFINITIONS
 */
// I2C_SCL時鐘
#define IIC_SCL_CLK         RCC_APB2Periph_GPIOB        // GPIO端口時鐘
#define IIC_SCL_PORT        GPIOB                       // GPIO端口
#define IIC_SCL_PIN         GPIO_Pin_6                  // GPIO引腳
// I2C_SDA時鐘
#define IIC_SDA_CLK         RCC_APB2Periph_GPIOB        // GPIO端口時鐘
#define IIC_SDA_PORT        GPIOB                       // GPIO端口
#define IIC_SDA_PIN         GPIO_Pin_5                  // GPIO引腳

/*********************************************************************
 * MACROS
 */
#define IIC_SCL_0()         GPIO_ResetBits(IIC_SCL_PORT, IIC_SCL_PIN) 
#define IIC_SCL_1()         GPIO_SetBits(IIC_SCL_PORT, IIC_SCL_PIN)
#define IIC_SDA_0()         GPIO_ResetBits(IIC_SDA_PORT, IIC_SDA_PIN) 
#define IIC_SDA_1()         GPIO_SetBits(IIC_SDA_PORT, IIC_SDA_PIN) 
#define IIC_SDA_READ()      GPIO_ReadInputDataBit(IIC_SDA_PORT, IIC_SDA_PIN) 

/*********************************************************************
 * API FUNCTIONS
 */
void IIC_Init(void);			 
void IIC_Start(void);
void IIC_Stop(void);
void IIC_SendByte(uint8_t ucByte);
uint8_t IIC_ReadByte(void);
uint8_t IIC_WaitAck(void);
void IIC_Ack(void);
void IIC_NAck(void);
uint8_t IIC_CheckDevice(uint8_t address);

#endif /* _BOARD_I2C_H_ */

三、API調用

需包含頭文件 board_i2c.h

IIC_Init

功能 I2C驅動初始化,採用模擬IO的方式實現
函數定義 void IIC_Init(void)
參數
返回

IIC_Start

功能 CPU發起I2C總線啓動信號
函數定義 void IIC_Start(void)
參數
返回

IIC_Stop

功能 CPU發起I2C總線停止信號
函數定義 void IIC_Stop(void)
參數
返回

IIC_SendByte

功能 CPU向I2C總線設備發送8bit數據
函數定義 void IIC_SendByte(uint8_t ucByte)
參數 ucByte:等待發送的字節
返回

IIC_ReadByte

功能 CPU從I2C總線設備讀取8bit數據
函數定義 uint8_t IIC_ReadByte(void)
參數
返回 讀到的數據

IIC_WaitAck

功能 CPU產生一個時鐘,並讀取器件的ACK應答信號
函數定義 uint8_t IIC_WaitAck(void)
參數
返回 0表示正確應答,1表示無器件響應

IIC_Ack

功能 CPU產生一個ACK信號
函數定義 void IIC_Ack(void)
參數
返回

IIC_NAck

功能 CPU產生1個NACK信號
函數定義 void IIC_NAck(void)
參數
返回

IIC_CheckDevice

功能 檢測I2C總線設備,CPU向發送設備地址,然後讀取設備應答來判斷該設備是否存在
函數定義 uint8_t IIC_CheckDevice(uint8_t address)
參數 address:設備的I2C總線地址+讀寫控制bit(0 = w, 1 = r)
返回 0 - 表示正確, 1 - 表示未探測到

四、使用例子

1)添加頭文件

#include "board_i2c.h"

2)添加初始化代碼

int main(void)
{	
    BaseType_t xReturn = pdPASS;                                        // 定義一個創建信息返回值,默認爲pdPASS
  
    /*-------------------------- 外設驅初始化 ---------------------------*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);                     // STM32中斷優先級分組爲4,即4bit都用來表示搶佔優先級,範圍爲:0~15優先級分組只需要分組一次即可,以後如果有其他的任務需要用到中斷,都統一用這個優先級分組,千萬不要再分組,切忌
    Board_LedInit();                                                    // LED燈驅動初始化
    Board_KeyInit();                                                    // 按鍵驅動初始化 
    USART_Config();                                                     // 串口驅動初始化    
  
    printf("-------------- I2C Init--------------\r\n");
    // 這裏寫入設備的I2C總線地址
    uint8_t i2cDevice = IIC_CheckDevice(TEA5767_ADDR_W);            // 初始化I2C,並檢測是否有設備
    if(!i2cDevice)
    {
        printf("---- I2C OK ----\r\n");  
    }   
    else
    {
        printf("---- I2C ERROR ----\r\n");  
    }  
  
    /*-------------------------- 任務創建 ---------------------------*/
    xReturn = xTaskCreate((TaskFunction_t)appTaskCreate,                // 任務入口函數
                          (const char*   )"appTaskCreate",              // 任務名字
                          (uint16_t      )512,                          // 任務棧大小
                          (void*         )NULL,                         // 任務入口函數參數
                          (UBaseType_t   )1,                            // 任務的優先級
                          (TaskHandle_t* )&s_appTaskCreateHandle);      // 任務控制塊指針
                          
    /*-------------------------- 開啓調度 ---------------------------*/           
    if(pdPASS == xReturn)
    {
        vTaskStartScheduler();                                      
    }
    else
    {
        return -1;
    }
  
    while(1)
    {
        /* 正常不會執行到這裏 */
    };   
}

• 由 Leung 寫於 2020 年 6 月 15 日

• 參考:【STM32】IIC的基本原理(實例:普通IO口模擬IIC時序讀取24C02)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章