模拟I2C 源码 (SDA要切换输入输出模式)
iic.c 源文件源码
/**@copyright Copyright(c)2014-2011 XXXX Co.,Ltd. All rights reserved.
*******************************************************************************
* @file
* @brief
* @author 匠在江湖 (ID:123456)
* @version V1.0
* @date 2014/05/08
*******************************************************************************
* @note
*
* @attention
*
*******************************************************************************
* @par 修改日志:
* <table>
* <tr><th>Date <th>Version <th>Author(ID:xxxxxx) <th>Description</tr>
* <tr><td>2014/05/08 <td>V1.0 <td>某某某(ID:123456) <td>创建初始版本</tr>
* </table>
*******************************************************************************
*/
/* Private includes ***********************************************************/
/* USER CODE BEGIN Includes */
#include "iic.h"
/* USER CODE END Includes */
/* Private typedef ************************************************************/
/* USER CODE BEGIN PT */
/* USER CODE END PT */
/* Private constants **********************************************************/
/* USER CODE BEGIN PC */
//此IIC通信速度约 100K 。以下参数依通信设备数据手册参数调整。
#define IIC_SCL_TLOW_US 6 //tLOW 时钟低电平周期,约100K
#define IIC_SCL_THIGH_US 3 //时钟高电平周期 tHIGH
#define IIC_STA_TSU_US 3 //tSU: STA 起始信号建立时间
#define IIC_STA_THD_US 3 //tHD: STA 起始信号保持时间
#define IIC_STO_TSU_US 3 //tSU: STO 停止信号建立时间
#define IIC_TBUF_US 6 //tBUF 新的发送开始前总线空闲时间
/* USER CODE END PC */
/* Private macro **************************************************************/
/* USER CODE BEGIN PM */
#define IIC_SCL_MODE_IN() GPIOB->MODER &= ~(3u<<(2*8)) //输入模式
#define IIC_SCL_MODE_OUT() GPIOB->MODER &= ~(3u<<(2*8)); GPIOB->MODER |= (1u<<(2*8)) //通用输出模式
#define IIC_SCL_PIN_OUT(x) PBout(8) = x
#define IIC_SDA_MODE_IN() GPIOB->MODER &= ~(3u<<(2*9)) //输入模式
#define IIC_SDA_MODE_OUT() GPIOB->MODER &= ~(3u<<(2*9)); GPIOB->MODER |= (1u<<(2*9)) //通用输出模式
#define IIC_SDA_PIN_OUT(x) PBout(9) = x
#define IIC_SDA_PIN_IN() PBin(9)
/* USER CODE END PM */
/* Private variables **********************************************************/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes ************************************************/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/**
*******************************************************************************
* @brief IIC 初始化 函数
* @param [in] None
* @return None
* @note
*******************************************************************************
*/
void IIC_Init(void)
{
RCC->AHB1ENR |= (1u << 1); //打开GPIOB的时钟
/* 配置GPIOx
输入寄存器 IDR
输出寄存器 ODR
配置模式 MODER (MODERy[1:0])
00:输入(复位状态)
01:通用输出模式
10:复用功能模式
11:模拟模式
输出类型 OTYPER (OTy)
0:输出推挽(复位状态)
1:输出开漏
输出速度 OSPEEDR (OSPEEDRy[1:0])
00:2 MHz(低速)
01:25 MHz(中速)
10:50 MHz(快速)
11:30 pF 时为 100 MHz(高速)(15 pF 时为 80 MHz 输出(最大速度))
配置上下拉 PUPDR (PUPDRy[1:0])
00:无上拉或下拉
01:上拉
10:下拉
复用功能低位寄存器 AFR[0] (AFRLy[3:0]) (GPIO PINy是0~7)
0000:AF0
......
0000:AF15
复用功能高位寄存器 AFR[1] (AFRLy[3:0]) (GPIO PINy是8~15)(位移减去32)
0000:AF0
......
0000:AF15
*/
//PB8(CLK):推挽输出
GPIOB->ODR |= (1u<< 8);
GPIOB->MODER &= ~(3u<<(2*8)); //清零,输入
GPIOB->MODER |= (1u<<(2*8)); //置位,通用输出模式
GPIOB->OTYPER &= ~(1u<< 8 ); //清零,输出推挽
GPIOB->OSPEEDR &= ~(3u<<(2*8)); //清零,2 MHz(低速)
//PB9(SDA):推挽输出
GPIOB->ODR |= (1u<< 9);
GPIOB->MODER &= ~(3u<<(2*9)); //清零,输入
GPIOB->MODER |= (1u<<(2*9)); //置位,通用输出模式
GPIOB->OTYPER &= ~(1u<< 9 ); //清零,输出推挽
GPIOB->OSPEEDR &= ~(3u<<(2*9)); //清零,2 MHz(低速)
}
/**
*******************************************************************************
* @brief IIC 延时微秒 函数
* @param [in] us
* @return None
* @note STM32 大部分指令一个时钟周期执行一条指令
*******************************************************************************
*/
static void IIC_DelayUS(unsigned char us)
{
unsigned char i;
while(us--)
{
i = 33; //一次循环大约5条汇编指令。168 / 5
while(i--);
}
}
/**
*******************************************************************************
* @brief IIC 起始条件 函数
* @param [in] None
* @return None
* @note 起始条件--SCL高电平时,SDA下降沿。(即开始传送数据)
* ______
* SCL \__
* ___
* SDA \_____
*
*******************************************************************************
*/
void IIC_Start(void)
{
IIC_SDA_PIN_OUT(1); //SDA高电平
IIC_SDA_MODE_OUT();
IIC_SCL_PIN_OUT(1); //SCL高电平
IIC_SCL_MODE_OUT();
IIC_DelayUS(IIC_STA_TSU_US); //起始信号建立时间 tSU:STA
IIC_SDA_PIN_OUT(0); //SDA下降沿
IIC_DelayUS(IIC_STA_THD_US); //起始信号保持时间 tHD:STA
IIC_SCL_PIN_OUT(0); //钳住I2C总线,准备发数据
}
/**
*******************************************************************************
* @brief IIC 发送数据 接收返回应答 函数
* @param [in] data
* @return 应答 IIC_ACK(0),非应答 IIC_NACK(1)
* @note 数据--SCL高电平时,SDA保持持不变;SCL低电平时,SDA允许变化。
* 应答--SCL第9个脉冲时,SDA位数据(也就是第9位数据)0 表示应答;反之非应答。
* ____ ____
* SCL ____/ \____ SCL ____/ \____
* ________
* SDA __/ \__ SDA ______________
* 位数据 1 位数据 0
*******************************************************************************
*/
unsigned char IIC_TxDataRxAck(unsigned char data)
{
unsigned char ack;
unsigned char i;
IIC_SCL_PIN_OUT(0);
IIC_SDA_MODE_OUT(); //SDA设为输出模式
for(i=0; i<8; i++)
{
if(data&0x80) //发位 位数据 1
{
IIC_SDA_PIN_OUT(1);
}
else //发位 位数据 0
{
IIC_SDA_PIN_OUT(0);
}
data <<= 1; //待发位数据,移到高位待发送
IIC_DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //SCL上升沿
IIC_DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
IIC_SCL_PIN_OUT(0); //SCL下降沿
}
IIC_SDA_MODE_IN(); //SDA设为输入模式
IIC_DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //第9个脉冲 SCL上升沿
IIC_DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
ack = IIC_SDA_PIN_IN(); //接收应答信号
IIC_SCL_PIN_OUT(0); //第9个脉冲 SCL下降沿
return ack; //返回应答
}
/**
*******************************************************************************
* @brief IIC 接收返回数据 发送应答 函数
* @param [in] ack 应答 IIC_ACK(0),非应答 IIC_NACK(1)
* @return data
* @note 数据--SCL高电平时,SDA保持持不变;SCL低电平时,SDA允许变化。
* 应答--SCL第9个脉冲时,SDA位数据(也就是第9位数据)0 表示应答;反之非应答。
* ____ ____
* SCL ____/ \____ SCL ____/ \____
* ________
* SDA __/ \__ SDA ______________
* 位数据 1 位数据 0
*******************************************************************************
*/
unsigned char IIC_RxDataTxAck(unsigned char ack)
{
unsigned char data;
unsigned char i;
IIC_SCL_PIN_OUT(0);
IIC_SDA_MODE_IN(); //SDA设为输入模式
for(i=0; i<8; i++)
{
IIC_DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //SCL上升沿
IIC_DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
data <<= 1; //留出低位,存放收的位数据
if( IIC_SDA_PIN_IN() ) //收到 位数据 1
{
data |= 0x01;
}
else //收到 位数据 0
{
data &= 0xfe;
}
IIC_SCL_PIN_OUT(0); //SCL下降沿
}
IIC_SDA_MODE_OUT(); //SDA设为输出模式
IIC_SDA_PIN_OUT(ack); //发送应答信号
IIC_DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //第9个脉冲 SCL上升沿
IIC_DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
IIC_SCL_PIN_OUT(0); //第9个脉冲 SCL下降沿
return data;
}
/**
*******************************************************************************
* @brief IIC 停止条件 函数
* @param [in] None
* @return None
* @note 停止条件--SCL高电平时,SDA上升沿。(即结束传送数据)
* _________
* SCL __/
* ______
* SDA _____/
*
*******************************************************************************
*/
void IIC_Stop(void)
{
IIC_SCL_PIN_OUT(0);
IIC_SDA_PIN_OUT(0);
IIC_SDA_MODE_OUT(); //SDA设为输出模式
IIC_DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //SCL高电平
IIC_DelayUS(IIC_STO_TSU_US); //停止信号建立时间 STO:tSU
IIC_SDA_PIN_OUT(1); //SDA上升沿,通信结束
IIC_DelayUS(IIC_TBUF_US); //新的发送开始前总线空闲时间 tBUF
}
iic.h 头文件源码
/**@copyright Copyright(c)2014-2011 XXXX Co.,Ltd. All rights reserved.
*******************************************************************************
* @file
* @brief
* @author 匠在江湖 (ID:123456)
* @version V1.0
* @date 2014/05/08
*******************************************************************************
* @note
*
* @attention
*
*******************************************************************************
* @par 修改日志:
* <table>
* <tr><th>Date <th>Version <th>Author(ID:xxxxxx) <th>Description</tr>
* <tr><td>2014/05/08 <td>V1.0 <td>某某某(ID:123456) <td>创建初始版本</tr>
* </table>
*******************************************************************************
*/
#ifndef IIC_H_
#define IIC_H_
/* Private includes ***********************************************************/
/* USER CODE BEGIN Includes */
#include "stm32f4xx.h"
#include "bit_band_stm32f4xx.h"
/* USER CODE END Includes */
#ifdef __cplusplus
extern "C" {
#endif
/* Exported typedef ***********************************************************/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants *********************************************************/
/* USER CODE BEGIN EC */
#define IIC_ACK 0 //应答
#define IIC_NACK 1 //非应答
/* USER CODE END EC */
/* Exported macro *************************************************************/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes **********************************************/
/* USER CODE BEGIN EFP */
extern void IIC_Init(void); //IIC 初始化 IO口
extern void IIC_Start(void); //IIC 起始条件
extern unsigned char IIC_TxDataRxAck(unsigned char data); //IIC 发送数据 接收返回应答
extern unsigned char IIC_RxDataTxAck(unsigned char ack); //IIC 接收返回数据 发送应答
extern void IIC_Stop(void); //IIC 停止条件
/* USER CODE END EFP */
#ifdef __cplusplus
}
#endif
#endif /* IIC_H_ */
模拟I2C 源码 (利用开漏输出,SDA不用切换输入输出模式了)
iic.c 源文件 源码
/**@copyright Copyright(c)2014-2011 XXXX Co.,Ltd. All rights reserved.
*******************************************************************************
* @file
* @brief
* @author 匠在江湖 (ID:123456)
* @version V1.0
* @date 2014/05/08
*******************************************************************************
* @note
*
* @attention
*
*******************************************************************************
* @par 修改日志:
* <table>
* <tr><th>Date <th>Version <th>Author(ID:xxxxxx) <th>Description</tr>
* <tr><td>2014/05/08 <td>V1.0 <td>某某某(ID:123456) <td>创建初始版本</tr>
* </table>
*******************************************************************************
*/
/* Private includes ***********************************************************/
/* USER CODE BEGIN Includes */
#include "iic.h"
/* USER CODE END Includes */
/* Private typedef ************************************************************/
/* USER CODE BEGIN PT */
/* USER CODE END PT */
/* Private constants **********************************************************/
/* USER CODE BEGIN PC */
//此IIC通信速度约 100K 。以下参数依通信设备数据手册参数调整。
#define IIC_SCL_TLOW_US 6 //tLOW 时钟低电平周期,约100K
#define IIC_SCL_THIGH_US 3 //时钟高电平周期 tHIGH
#define IIC_STA_TSU_US 3 //tSU: STA 起始信号建立时间
#define IIC_STA_THD_US 3 //tHD: STA 起始信号保持时间
#define IIC_STO_TSU_US 3 //tSU: STO 停止信号建立时间
#define IIC_TBUF_US 6 //tBUF 新的发送开始前总线空闲时间
/* USER CODE END PC */
/* Private macro **************************************************************/
/* USER CODE BEGIN PM */
#define IIC_SCL_PIN_OUT(x) PBout(8) = x
#define IIC_SDA_PIN_OUT(x) PBout(9) = x
#define IIC_SDA_PIN_IN() PBin(9)
/* USER CODE END PM */
/* Private variables **********************************************************/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes ************************************************/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/**
*******************************************************************************
* @brief IIC 初始化 函数
* @param [in] None
* @return None
* @note
*******************************************************************************
*/
void IIC_Init(void)
{
RCC->AHB1ENR |= (1u << 1); //打开GPIOB的时钟
/* 配置GPIOx
输入寄存器 IDR
输出寄存器 ODR
配置模式 MODER (MODERy[1:0])
00:输入(复位状态)
01:通用输出模式
10:复用功能模式
11:模拟模式
输出类型 OTYPER (OTy)
0:输出推挽(复位状态)
1:输出开漏
输出速度 OSPEEDR (OSPEEDRy[1:0])
00:2 MHz(低速)
01:25 MHz(中速)
10:50 MHz(快速)
11:30 pF 时为 100 MHz(高速)(15 pF 时为 80 MHz 输出(最大速度))
配置上下拉 PUPDR (PUPDRy[1:0])
00:无上拉或下拉
01:上拉
10:下拉
复用功能低位寄存器 AFR[0] (AFRLy[3:0]) (GPIO PINy是0~7)
0000:AF0
......
0000:AF15
复用功能高位寄存器 AFR[1] (AFRLy[3:0]) (GPIO PINy是8~15)(位移减去32)
0000:AF0
......
0000:AF15
*/
//PB8(CLK):推挽输出
GPIOB->ODR |= (1u<< 8);
GPIOB->MODER &= ~(3u<<(2*8)); //清零,输入
GPIOB->MODER |= (1u<<(2*8)); //置位,通用输出模式
GPIOB->OTYPER &= ~(1u<< 8 ); //清零,输出推挽
GPIOB->OSPEEDR &= ~(3u<<(2*8)); //清零,2 MHz(低速)
//PB9(SDA):开漏输出,上拉
GPIOB->ODR |= (1u<< 9);
GPIOB->MODER &= ~(3u<<(2*9)); //清零,输入
GPIOB->MODER |= (1u<<(2*9)); //置位,通用输出模式
GPIOB->OTYPER |= (1u<< 9 ); //清零,开漏输出
GPIOB->OSPEEDR &= ~(3u<<(2*9)); //清零,2 MHz(低速)
GPIOB->PUPDR &= ~(3u<<(2*9)); //清零,无上拉或下拉
GPIOB->PUPDR |= (1u<<(2*9)); //置位,上拉
}
/**
*******************************************************************************
* @brief IIC 起始条件 函数
* @param [in] None
* @return None
* @note 起始条件--SCL高电平时,SDA下降沿。(即开始传送数据)
* ______
* SCL \__
* ___
* SDA \_____
*
*******************************************************************************
*/
void IIC_Start(void)
{
IIC_SDA_PIN_OUT(1); //SDA高电平
IIC_SCL_PIN_OUT(1); //SCL高电平
DelayUS(IIC_STA_TSU_US); //起始信号建立时间 tSU:STA
IIC_SDA_PIN_OUT(0); //SDA下降沿
DelayUS(IIC_STA_THD_US); //起始信号保持时间 tHD:STA
IIC_SCL_PIN_OUT(0); //钳住I2C总线,准备发数据
}
/**
*******************************************************************************
* @brief IIC 发送数据 接收返回应答 函数
* @param [in] data
* @return 应答 IIC_ACK(0),非应答 IIC_NACK(1)
* @note 数据--SCL高电平时,SDA保持持不变;SCL低电平时,SDA允许变化。
* 应答--SCL第9个脉冲时,SDA位数据(也就是第9位数据)0 表示应答;反之非应答。
* ____ ____
* SCL ____/ \____ SCL ____/ \____
* ________
* SDA __/ \__ SDA ______________
* 位数据 1 位数据 0
*******************************************************************************
*/
unsigned char IIC_TxDataRxAck(unsigned char data)
{
unsigned char ack;
unsigned char i;
IIC_SCL_PIN_OUT(0);
for(i=0; i<8; i++)
{
if(data&0x80) //发位 位数据 1
{
IIC_SDA_PIN_OUT(1);
}
else //发位 位数据 0
{
IIC_SDA_PIN_OUT(0);
}
data <<= 1; //待发位数据,移到高位待发送
DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //SCL上升沿
DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
IIC_SCL_PIN_OUT(0); //SCL下降沿
}
IIC_SDA_PIN_OUT(1); //SDA高电平,释放总线
DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //第9个脉冲 SCL上升沿
DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
ack = IIC_SDA_PIN_IN(); //接收应答信号
IIC_SCL_PIN_OUT(0); //第9个脉冲 SCL下降沿
return ack; //返回应答
}
/**
*******************************************************************************
* @brief IIC 接收返回数据 发送应答 函数
* @param [in] ack 应答 IIC_ACK(0),非应答 IIC_NACK(1)
* @return data
* @note 数据--SCL高电平时,SDA保持持不变;SCL低电平时,SDA允许变化。
* 应答--SCL第9个脉冲时,SDA位数据(也就是第9位数据)0 表示应答;反之非应答。
* ____ ____
* SCL ____/ \____ SCL ____/ \____
* ________
* SDA __/ \__ SDA ______________
* 位数据 1 位数据 0
*******************************************************************************
*/
unsigned char IIC_RxDataTxAck(unsigned char ack)
{
unsigned char data;
unsigned char i;
IIC_SCL_PIN_OUT(0);
IIC_SDA_PIN_OUT(1); //SDA高电平,释放总线
for(i=0; i<8; i++)
{
DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //SCL上升沿
DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
data <<= 1; //留出低位,存放收的位数据
if( IIC_SDA_PIN_IN() ) //收到 位数据 1
{
data |= 0x01;
}
else //收到 位数据 0
{
data &= 0xfe;
}
IIC_SCL_PIN_OUT(0); //SCL下降沿
}
IIC_SDA_PIN_OUT(ack); //发送应答信号
DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //第9个脉冲 SCL上升沿
DelayUS(IIC_SCL_THIGH_US); //时钟高电平周期 tHIGH
IIC_SCL_PIN_OUT(0); //第9个脉冲 SCL下降沿
return data;
}
/**
*******************************************************************************
* @brief IIC 停止条件 函数
* @param [in] None
* @return None
* @note 停止条件--SCL高电平时,SDA上升沿。(即结束传送数据)
* _________
* SCL __/
* ______
* SDA _____/
*
*******************************************************************************
*/
void IIC_Stop(void)
{
IIC_SCL_PIN_OUT(0);
IIC_SDA_PIN_OUT(0);
DelayUS(IIC_SCL_TLOW_US); //时钟高电平周期 tLOW
IIC_SCL_PIN_OUT(1); //SCL高电平
DelayUS(IIC_STO_TSU_US); //停止信号建立时间 STO:tSU
IIC_SDA_PIN_OUT(1); //SDA上升沿,通信结束
DelayUS(IIC_TBUF_US); //新的发送开始前总线空闲时间 tBUF
}
iic.h 头文件 源
/**@copyright Copyright(c)2014-2011 XXXX Co.,Ltd. All rights reserved.
*******************************************************************************
* @file
* @brief
* @author 匠在江湖 (ID:123456)
* @version V1.0
* @date 2014/05/08
*******************************************************************************
* @note
*
* @attention
*
*******************************************************************************
* @par 修改日志:
* <table>
* <tr><th>Date <th>Version <th>Author(ID:xxxxxx) <th>Description</tr>
* <tr><td>2014/05/08 <td>V1.0 <td>某某某(ID:123456) <td>创建初始版本</tr>
* </table>
*******************************************************************************
*/
#ifndef IIC_H_
#define IIC_H_
/* Private includes ***********************************************************/
/* USER CODE BEGIN Includes */
#include "stm32f4xx.h"
#include "bit_band_stm32f4xx.h"
#include "delay.h"
/* USER CODE END Includes */
#ifdef __cplusplus
extern "C" {
#endif
/* Exported typedef ***********************************************************/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants *********************************************************/
/* USER CODE BEGIN EC */
#define IIC_ACK 0 //应答
#define IIC_NACK 1 //非应答
/* USER CODE END EC */
/* Exported macro *************************************************************/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes **********************************************/
/* USER CODE BEGIN EFP */
extern void IIC_Init(void); //IIC 初始化 IO口
extern void IIC_Start(void); //IIC 起始条件
extern unsigned char IIC_TxDataRxAck(unsigned char data); //IIC 发送数据 接收返回应答
extern unsigned char IIC_RxDataTxAck(unsigned char ack); //IIC 接收返回数据 发送应答
extern void IIC_Stop(void); //IIC 停止条件
/* USER CODE END EFP */
#ifdef __cplusplus
}
#endif
#endif /* IIC_H_ */