软件I2C
硬件IIC读取写入函数
HAL_I2C_Mem_Read(&hi2cx, DEV_ADD, REG_ADD, ADD_SIZE, &DATA, DATA_SIZE, HAL_MAX_DELAY);
HAL_I2C_Mem_Write(&hi2c, DEV_ADD, REG_ADD, ADD_SIZE, &DATA, DATA_SIZE, HAL_MAX_DELAY);
HAL_I2C_Master_Transmit(&hi2c, DEV_ADD, &DATA, DATA_SIZE, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(&hi2c, DEV_ADD, &DATA, DATA_SIZE, HAL_MAX_DELAY);
模拟IIC读取写入函数及其实现
uint8_t Soft_IIC_Read_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t *pData) {
Soft_IIC_Start();
if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
Soft_IIC_Stop();
return 0;
}
if (!Soft_IIC_Write_Byte(regAddr)) {
Soft_IIC_Stop();
return 0;
}
Soft_IIC_Start();
if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x01)) {
Soft_IIC_Stop();
return 0;
}
*pData = Soft_IIC_Recv_Byte(NACK);
Soft_IIC_Stop();
return 1;
}
uint8_t Soft_IIC_Write_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t data) {
Soft_IIC_Start();
if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
Soft_IIC_Stop();
return 0;
}
if (!Soft_IIC_Write_Byte(regAddr)) {
Soft_IIC_Stop();
return 0;
}
if (!Soft_IIC_Write_Byte(data)) {
Soft_IIC_Stop();
return 0;
}
Soft_IIC_Stop();
return 1;
}
IIC写入数据,在初始化传感器的时候使用,比如配置MPU6050的精度,量程范围等等。
IIC写入流程:
IIC开始。
IIC发送传感器地址并检测是否响应。
IIC发送寄存器地址并检测是否响应。
IIC发送寄存器地址参数数据。
IIC停止。
IIC读取流程:
IIC开始。
IIC发送传感器地址并检测是否响应。
IIC发送寄存器地址并检测是否响应。
IIC开始。
IIC发送接收请求并检测是否响应。(从代码里面可以看见接收请求就是传感器地址&&0x01
)
IIC接收数据。
IIC停止。
逐步剖析
IIC启动
void Soft_IIC_Start(void)
{
Soft_IIC_Output();
IIC_SCL_L();//至于这个为什么要加,暂时不清楚
IIC_SDA_H();
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SDA_L();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_L();
}
IIC写入
设置IO口为输出模式
把8位字节按高低电平发送
等待回应(等待回应函数中设置了IO为输入模式,等待的是数据口的电平变化)
uint8_t Soft_IIC_Write_Byte(uint8_t Byte)
{
uint8_t i;
Soft_IIC_Output();
for (i = 0x80; i != 0; i >>= 1)
{
if (Byte & i)
{
Soft_IIC_Write_High();
}
else
{
Soft_IIC_Write_Low();
}
}
return (Soft_IIC_Wait_ACK());
}
等待回应函数
这里是 模拟IIC 的重点。
在源代码中,Soft_IIC_Input()实际上我把 IO 配置为输入模式这个代码注释了,仅仅把 IO 配置为高电平。
当然了,这个SDA引脚默认是输出模式,为什么把 IO 输出模式依然能读取外界电平,实现输出又输入呢。
https://blog.csdn.net/qq_35629563/article/details/87710101
其实 很多模拟IIC教程会让你把引脚配置为开漏输出,却不说为什么。
这是原因:
- 引脚配置成开漏输出
- 外部上拉
- 在输入的时候,要先将SDA的引脚置高
经过这三样配置,IIC 就能实现输入又输出。如果SDA 引脚置低可能就只能输出模式了吧。
uint8_t Soft_IIC_Wait_ACK(void)
{
uint8_t wait;
Soft_IIC_Output();
IIC_SDA_H();
Soft_IIC_Input();
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
while (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN))
{
wait++;
if (wait > 200)
{
Soft_IIC_Stop();
return 0;
}
}
IIC_SCL_L();
return 1;
}
IIC停止函数
void Soft_IIC_Stop(void)
{
Soft_IIC_Output();
IIC_SCL_L();
IIC_SCL_H();
IIC_SDA_L();
IIC_Delay(IIC_DELAY_TIME);
IIC_SDA_H();
IIC_Delay(IIC_DELAY_TIME);
}
总代码
H文件
#ifndef _SOFTWAREIIC_H_
#define _SOFTWAREIIC_H_
#include "main.h"
typedef enum{
NACK = 0,
ACK = 1
}ACK_STATUS;
#define IIC_DELAY_TIME 10
// 软件IIC引脚定义
#define IIC_SCL_PIN GPIO_PIN_2
#define IIC_SCL_PORT GPIOB
#define IIC_SDA_PIN GPIO_PIN_1
#define IIC_SDA_PORT GPIOB
// #define IIC_SDA_H() GPIOB->BSRR = 0x010
// #define IIC_SDA_L() GPIOB->BRR = 0x010
// #define IIC_SCL_H() GPIOB->BSRR = 0x100
// #define IIC_SCL_L() GPIOB->BRR = 0x100
#define IIC_SCL_H() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET)
#define IIC_SCL_L() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET)
#define IIC_SDA_H() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET)
#define IIC_SDA_L() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET)
void Soft_IIC_Init(void);
void Soft_IIC_Start(void);
void Soft_IIC_Stop(void);
void Soft_IIC_ACK(void);
void Soft_IIC_NACK(void);
uint8_t Soft_IIC_Wait_ACK(void);
uint8_t Soft_IIC_Write_Byte(uint8_t Byte);
uint8_t Soft_IIC_Recv_Byte(ACK_STATUS ack_sta);
uint8_t Soft_IIC_Read_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t *pData);
uint8_t Soft_IIC_Write_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t data);
#endif
C文件
/**
* @file softwareiic.c
* @brief 适用于STM32 HAL库及CUBEMX生成工程的软件模拟IIC
* @version V1.0.0
* @author Sundea
* @date 2023/5/8
*/
#include "softwareiic.h"
#include "sys_delay_us.h"
/**
* @brief IIC延时
* @param 无
* @return 无
*/
void IIC_Delay(uint8_t time)
{
delay_us(time);
}
/**
* @brief IIC初始化
* @param 无
* @return 无
*/
void Soft_IIC_Init(void)
{
IIC_SDA_H();
IIC_SCL_H();
}
/**
* @brief SDA引脚设置输出模式
* @param 无
* @return 无
*/
static void Soft_IIC_Output(void)
{
GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT;
SOFT_IIC_GPIO_STRUCT.Mode = GPIO_MODE_OUTPUT_PP;
SOFT_IIC_GPIO_STRUCT.Pin = IIC_SDA_PIN;
SOFT_IIC_GPIO_STRUCT.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(IIC_SDA_PORT, &SOFT_IIC_GPIO_STRUCT);
}
/**
* @brief SDA引脚设置输入模式
* @param 无
* @return 无
*/
static void Soft_IIC_Input(void)
{
GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT;
SOFT_IIC_GPIO_STRUCT.Mode = GPIO_MODE_INPUT;
SOFT_IIC_GPIO_STRUCT.Pin = IIC_SDA_PIN;
SOFT_IIC_GPIO_STRUCT.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(IIC_SDA_PORT, &SOFT_IIC_GPIO_STRUCT);
}
/**
* @brief IIC起始信号
* @param 无
* @return 无
*/
void Soft_IIC_Start(void)
{
Soft_IIC_Output();
IIC_SCL_L();
IIC_SDA_H();
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SDA_L();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_L();
}
/**
* @brief IIC停止信号
* @param 无
* @return 无
*/
void Soft_IIC_Stop(void)
{
Soft_IIC_Output();
IIC_SCL_L();
IIC_SDA_L();
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SDA_H();
IIC_Delay(IIC_DELAY_TIME);
}
/**
* @brief IIC应答信号
* @param 无
* @return 无
*/
void Soft_IIC_ACK(void)
{
Soft_IIC_Output();
IIC_SCL_L();
IIC_SDA_L();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_L();
}
/**
* @brief IIC无应答信号
* @param 无
* @return 无
*/
void Soft_IIC_NACK(void)
{
Soft_IIC_Output();
IIC_SCL_L();
IIC_SDA_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
}
/**
* @brief IIC等待应答信号
* @param 无
* @return 0无应答 1有应答
*/
uint8_t Soft_IIC_Wait_ACK(void)
{
uint8_t wait;
Soft_IIC_Output();
IIC_SDA_H();
Soft_IIC_Input();
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
while (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN))
{
wait++;
if (wait > 200)
{
Soft_IIC_Stop();
return 0;
}
}
IIC_SCL_L();
return 1;
}
/**
* @brief IIC写数据1
* @param 无
* @return 无
*/
void Soft_IIC_Write_High(void)
{
IIC_SCL_L();
IIC_SDA_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_L();
}
/**
* @brief IIC写数据0
* @param 无
* @return 无
*/
void Soft_IIC_Write_Low(void)
{
IIC_SCL_L();
IIC_SDA_L();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_L();
}
/**
* @brief IIC写入单个数据
* @param 无
* @return 应答信号, 0无应答 1有应答
*/
uint8_t Soft_IIC_Write_Byte(uint8_t Byte)
{
uint8_t i;
Soft_IIC_Output();
for (i = 0x80; i != 0; i >>= 1)
{
if (Byte & i)
{
Soft_IIC_Write_High();
}
else
{
Soft_IIC_Write_Low();
}
}
return (Soft_IIC_Wait_ACK());
}
/**
* @brief IIC读一个数据
* @param ACK:应答 NACK:不应答
* @return 返回读到的数据
*/
uint8_t Soft_IIC_Recv_Byte(ACK_STATUS ack_sta)
{
uint8_t data = 0, i;
Soft_IIC_Input();
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
for (i = 0x80; i != 0; i >>= 1)
{
if (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN) == 1)
{
data |= i;
}
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_L();
IIC_Delay(IIC_DELAY_TIME);
IIC_SCL_H();
IIC_Delay(IIC_DELAY_TIME);
}
if (ack_sta == ACK)
{
Soft_IIC_ACK();
}
else
{
Soft_IIC_NACK();
}
return data;
}
uint8_t Soft_IIC_Read_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t *pData) {
// Generate I2C start signal
Soft_IIC_Start();
// Write device address (shifted left by 1, indicating write operation)
if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
// Handle error if necessary
Soft_IIC_Stop();
return 0;
}
// Write register address
if (!Soft_IIC_Write_Byte(regAddr)) {
// Handle error if necessary
Soft_IIC_Stop();
return 0;
}
// Generate repeated start signal
Soft_IIC_Start();
// Write device address (shifted left by 1, indicating read operation)
if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x01)) {
// Handle error if necessary
Soft_IIC_Stop();
return 0;
}
// Read data from the specified register
*pData = Soft_IIC_Recv_Byte(NACK); // For the last byte, send NACK
// Generate I2C stop signal
Soft_IIC_Stop();
return 1; // Successful read operation
}
uint8_t Soft_IIC_Write_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t data) {
// Generate I2C start signal
Soft_IIC_Start();
// Write device address (shifted left by 1, indicating write operation)
if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
// Handle error if necessary
Soft_IIC_Stop();
return 0;
}
// Write register address
if (!Soft_IIC_Write_Byte(regAddr)) {
// Handle error if necessary
Soft_IIC_Stop();
return 0;
}
// Write data to the specified register
if (!Soft_IIC_Write_Byte(data)) {
// Handle error if necessary
Soft_IIC_Stop();
return 0;
}
// Generate I2C stop signal
Soft_IIC_Stop();
return 1; // Successful write operation
}