一、硬件连接
功能口 | 引脚 |
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)
{
/* 正常不会执行到这里 */
};
}