一、硬件連接
功能口 | 引腳 |
---|---|
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)添加頭文件
#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 日