前言
之前一个STM32项目使用的I2C的驱动代码,基于STM32标准库,当然,改为目前流行的HAL库,也不难。
代码
i2c_drv.h
#ifndef __I2C_DRV_H__
#define __I2C_DRV_H__
#include "stm32f10x.h"
#define I2C_ACK 0 //应答
#define I2C_NOACK 1 //非应答
/* -- GPIO-I2C宏定义 ----*/
#define I2C_SCL_RCC RCC_APB2Periph_GPIOB
#define I2C_SCL_GPIO_PORT GPIOB
#define I2C_SCL_GPIO_PINS GPIO_Pin_8
#define I2C_SDA_RCC RCC_APB2Periph_GPIOB
#define I2C_SDA_GPIO_PORT GPIOB
#define I2C_SDA_GPIO_PINS GPIO_Pin_9
#define I2C_SCL_LOW GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PINS)
#define I2C_SCL_HIGH GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PINS)
#define I2C_SDA_LOW GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PINS)
#define I2C_SDA_HIGH GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PINS)
#define I2C_SDA_READ GPIO_ReadInputDataBit(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PINS)
void I2C_Initializes(void);
void I2C_GPIO_DeInit(void);
void I2C_GPIO_Config(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_PutAck(uint8_t Ack);
uint8_t I2C_GetAck(void);
uint8_t I2C_WriteByte(uint8_t Data);
uint8_t I2C_ReadByte(uint8_t ack);
#endif
i2c_drv.c
#include "i2c_drv.h"
/************************************************
函数名称 : I2C_Delay
功 能 : I2C延时(非标准延时,请根据MCU速度 调节大小)
参 数 : 无
返 回 值 : 无
作 者 :
*************************************************/
static void I2C_Delay(void)
{
uint16_t cnt = 10;
while(cnt--)
{
__NOP();
}
}
/************************************************
函数名称 : I2C_GPIO_Config
功 能 : I2C引脚配置(开漏输出)
参 数 : 无
返 回 值 : 无
作 者 :
*************************************************/
void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(I2C_SCL_RCC | I2C_SDA_RCC, ENABLE);//打开GPIO时钟
GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // OD输出
GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 推挽输出
GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
}
/************************************************
函数名称 : I2C_GPIO_DeInit
功 能 : I2C引脚配置(输出HIGH)
参 数 : 无
返 回 值 : 无
作 者 :
*************************************************/
void I2C_GPIO_DeInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(I2C_SCL_RCC | I2C_SDA_RCC, ENABLE);//打开GPIO时钟
GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // PP输出
GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
I2C_SCL_HIGH; //置位状态
I2C_SDA_HIGH;
}
/************************************************
函数名称 : I2C_Initializes
功 能 : I2C初始化
参 数 : 无
返 回 值 : 无
作 者 :
*************************************************/
void I2C_Initializes(void)
{
I2C_GPIO_Config();
I2C_SCL_HIGH; //置位状态
I2C_SDA_HIGH;
}
/************************************************
函数名称 : I2C_Start
功 能 : I2C开始
参 数 : 无
返 回 值 : 无
作 者 :
*************************************************/
void I2C_Start(void)
{
I2C_SCL_HIGH;
I2C_Delay();
I2C_SDA_HIGH;
I2C_Delay();
I2C_SDA_LOW;
I2C_Delay();
I2C_SCL_LOW;
I2C_Delay();
}
/************************************************
函数名称 : I2C_Stop
功 能 : I2C停止
参 数 : 无
返 回 值 : 无
作 者 :
*************************************************/
void I2C_Stop(void)
{
I2C_SDA_LOW;
I2C_Delay();
I2C_SCL_HIGH;
I2C_Delay();
I2C_SDA_HIGH;
I2C_Delay();
I2C_Delay();
}
/************************************************
函数名称 : I2C_PutAck
功 能 : I2C主机产生应答(或非应答)位
参 数 : I2C_ACK ----- 应答
I2C_NOACK --- 非应答
返 回 值 : 无
作 者 :
*************************************************/
void I2C_PutAck(uint8_t Ack)
{
I2C_SCL_LOW;
I2C_Delay();
if(I2C_ACK == Ack)
I2C_SDA_LOW; //应答
else
I2C_SDA_HIGH; //非应答
I2C_Delay();
I2C_SCL_HIGH; //SCL高 -> 低
I2C_Delay();
I2C_Delay();
I2C_SCL_LOW; //SCL低
I2C_Delay();
I2C_Delay();
}
/************************************************
函数名称 : I2C_GetAck
功 能 : I2C主机读取应答(或非应答)位
参 数 : 无
返 回 值 : I2C_ACK ----- 应答
I2C_NOACK --- 非应答
作 者 :
*************************************************/
uint8_t I2C_GetAck(void)
{
uint8_t ack = I2C_NOACK;
I2C_SCL_LOW;
I2C_Delay();
I2C_Delay();
I2C_SDA_HIGH;
I2C_Delay();
I2C_SCL_HIGH; //SCL高(读取应答位)
I2C_Delay();
I2C_Delay();
if(I2C_SDA_READ)
ack = I2C_NOACK; //非应答
else
ack = I2C_ACK; //应答
I2C_SCL_LOW; //SCL低
I2C_Delay();
I2C_Delay();
return ack;
}
/************************************************
函数名称 : I2C_WriteByte
功 能 : I2C写一字节
参 数 : Data --- 数据
返 回 值 : 无
作 者 :
*************************************************/
uint8_t I2C_WriteByte(uint8_t Data)
{
uint8_t cnt = 0;
for(cnt=0; cnt<8; cnt++)
{
I2C_SCL_LOW;
I2C_Delay();
if(Data & 0x80)
I2C_SDA_HIGH; //SDA高
else
I2C_SDA_LOW; //SDA低
Data <<= 1;
I2C_Delay();
I2C_SCL_HIGH;
I2C_Delay();
I2C_Delay();
}
I2C_SCL_LOW;
I2C_Delay();
return I2C_GetAck();
}
/************************************************
函数名称 : I2C_ReadByte
功 能 : I2C读一字节
参 数 : ack --------- 产生应答(或者非应答)位
返 回 值 : data -------- 读取的一字节数据
作 者 :
*************************************************/
uint8_t I2C_ReadByte(uint8_t ack)
{
uint8_t cnt = 0;
uint8_t data = 0;
I2C_SCL_LOW;
I2C_Delay();
I2C_Delay();
I2C_SDA_HIGH;
I2C_Delay();
for(cnt=0; cnt<8; cnt++)
{
I2C_SCL_HIGH;
I2C_Delay();
I2C_Delay();
data <<= 1;
if(I2C_SDA_READ)
data |= 0x01;
I2C_SCL_LOW;
I2C_Delay();
I2C_Delay();
}
I2C_PutAck(ack);
return data;
}
使用实例
三轴G-Sensor寄存器读写
#define G_SLAVEADDR_WR 0x4C
#define G_SLAVEADDR_RD 0x4D
/******************************************
MSA300_WriteBytes
*******************************************/
void MSA300_WriteBytes(uint8_t RegAddr, uint8_t Data)
{
I2C_Start(); //开始数据传递
I2C_WriteByte(G_SLAVEADDR_WR); //写入从机地址
I2C_WriteByte(RegAddr); //写入读写命令
I2C_WriteByte(Data); //向当前地址(上面指定的地址)写入数据
I2C_Stop(); //停止数据传递
}
/******************************************
BMA220 read bytes
*******************************************/
uint8_t MSA300_ReadBytes(uint8_t RegAddr)
{
unsigned char ReadValue;
I2C_Start(); //开始数据传递
I2C_WriteByte(G_SLAVEADDR_WR); //选择从机地址
I2C_WriteByte(RegAddr); //写入寄存器地址
I2C_Start(); //开始数据传递
I2C_WriteByte(G_SLAVEADDR_RD); //选择从机地址
ReadValue=I2C_ReadByte(I2C_NOACK); //在指定地址读取数据
I2C_Stop(); //停止数据传递
return(ReadValue); //将指定地址读出的数据返回
}
总结
- 可以使用软件I2C,读取一些简单的如G-sensor之类的传感器。
- 注意I2C总线,外部必须有上拉电阻,GPIO配置为OD。
- I2C时序,速率,可以通过逻辑分析仪查看,修改GPIO 引脚速度、更改延时。