天天看點

STM32F103學習筆記(1)——FreeRTOS下模拟I2C一、硬體連接配接二、移植檔案2.1 board_i2c.c2.2 board_i2c.h三、API調用四、使用例子

一、硬體連接配接

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

二、移植檔案

連結:https://pan.baidu.com/s/1wxbQTMlnX2pavrbW2RYg4g 提取碼:dxex

将 board_i2c.c 和 board_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)添加頭檔案

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)

繼續閱讀