在HAL库项目开发过程中,偶尔会用到软件IIC。 同时因为部分开发者认为硬件IIC会遇到问题,需要重置。
bsp_i2c_soft.h
#ifndef __BSP_I2C_SOFT_H
#define __BSP_I2C_SOFT_H
#include "stm32f4xx_hal.h"
#include "main.h"
// #define SCL_GPIO_PORT GPIOB
// #define SCL_PIN GPIO_PIN_6
// #define SDA_GPIO_PORT GPIOB
// #define SDA_PIN GPIO_PIN_7
typedef enum {
IIC_OK = 0x00, //IIC工作正常
IIC_WAIT_ACK_ERR = 0x01, //slave设备返回ACK错误
IIC_WRITE_ERR = 0x02, //向slave设备写入错误
IIC_READ_ERR = 0x04 //从slave设备读取错误
} IIC_STATUS;
typedef struct
{
GPIO_TypeDef *scl_port; //定义IIC时钟使用的端口
uint16_t scl_pin; //定义IIC时钟使用的PIN脚
GPIO_TypeDef *sda_port; //定义IIC数据使用的端口
uint16_t sda_pin; //定义IIC数据使用的PIN脚
uint8_t sda_pin_num; //定义延时,以适应不同器件对速率的不同要求,具体值要在调试中确定
uint8_t addr; //器件地址
uint32_t delaytick; //定义延时,以适应不同器件对速率的不同要求,具体值要在调试中确定
}IIC_SOFT_TypeDef;
//IO方向设置
#define SDA_IN() {SDA_GPIO_PORT->MODER&=~(3<<(7*2));SDA_GPIO_PORT->MODER|=0<<7*2;} //PB7输入模式
#define SDA_OUT() {SDA_GPIO_PORT->MODER&=~(3<<(7*2));SDA_GPIO_PORT->MODER|=1<<7*2;} //PB7输出模式
#define GPIO_WRITE_PIN(PORT,PIN,VALUE) HAL_GPIO_WritePin(PORT,PIN,(VALUE)?GPIO_PIN_SET:GPIO_PIN_RESET)
#define GPIO_READ_PIN(PORT,PIN) HAL_GPIO_ReadPin(PORT,PIN)
//IIC所有操作函数
// void IIC_Init( ); //初始化IIC的IO口
void IIC_Init(const IIC_SOFT_TypeDef *pSoftIIC); //初始化IIC的IO口
int IIC_READ_SDA_Pin(const IIC_SOFT_TypeDef *pSoftIIC);
int IIC_SDA_Mode_In(const IIC_SOFT_TypeDef *pSoftIIC);
int IIC_SDA_Mode_Out(const IIC_SOFT_TypeDef *pSoftIIC);
void IIC_Start(const IIC_SOFT_TypeDef *pSoftIIC); //发送IIC开始信号
void IIC_Stop(const IIC_SOFT_TypeDef *pSoftIIC); //发送IIC停止信号
void IIC_Send_Byte(const IIC_SOFT_TypeDef *pSoftIIC,uint8_t txd); //IIC发送一个字节
// uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
uint8_t IIC_Read_Byte( const IIC_SOFT_TypeDef *pSoftIIC);//IIC读取一个字节
uint8_t IIC_Wait_Ack(const IIC_SOFT_TypeDef *pSoftIIC); //IIC等待ACK信号
void IIC_Ack(const IIC_SOFT_TypeDef *pSoftIIC); //IIC发送ACK信号
void IIC_NAck(const IIC_SOFT_TypeDef *pSoftIIC); //IIC不发送ACK信号
uint8_t IIC_Read_Test(const IIC_SOFT_TypeDef *pSoftIIC, uint8_t reg_addr, uint8_t *buf, uint8_t len);
uint8_t IIC_Write_Test(const IIC_SOFT_TypeDef *pSoftIIC , uint8_t reg_addr, uint8_t *buf, uint8_t len);
#endif
bsp_i2c_soft.c
#include "bsp_i2c_soft.h"
#include "elog.h"
// #include "delay.h"
void IIC_Init(const IIC_SOFT_TypeDef *pSoftIIC )
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
//PB6 SCL PB7初始化设置
GPIO_Initure.Pin = pSoftIIC->scl_pin ;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; // 上拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; // 快速
HAL_GPIO_Init( pSoftIIC->scl_port, &GPIO_Initure );
// SDA PB7 初始化设置
GPIO_Initure.Pin = pSoftIIC->sda_pin;
HAL_GPIO_Init( pSoftIIC->sda_port, &GPIO_Initure );
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
}
int IIC_READ_SDA_Pin(const IIC_SOFT_TypeDef *pSoftIIC)
{
return HAL_GPIO_ReadPin(pSoftIIC->sda_port,pSoftIIC->sda_pin);
}
int IIC_SDA_Mode_In(const IIC_SOFT_TypeDef *pSoftIIC)
{
// 取针脚号 pin6 的值为 2^6
// GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);
pSoftIIC->sda_port->MODER&=~(3<<(pSoftIIC->sda_pin_num*2));
pSoftIIC->sda_port->MODER|=0<<pSoftIIC->sda_pin_num*2;
}
int IIC_SDA_Mode_Out(const IIC_SOFT_TypeDef *pSoftIIC)
{
// GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);
pSoftIIC->sda_port->MODER&=~(3<<(pSoftIIC->sda_pin_num*2));
pSoftIIC->sda_port->MODER|=1<<pSoftIIC->sda_pin_num*2;
}
//产生IIC起始信号
void IIC_Start(const IIC_SOFT_TypeDef *pSoftIIC)
{
// SDA_OUT();//sda线输出
IIC_SDA_Mode_Out( pSoftIIC); //sda线输出
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
delay_us(4);
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 0); //START:when CLK is high,DATA change form high to low
delay_us(4);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(const IIC_SOFT_TypeDef *pSoftIIC)
{
// SDA_OUT();//sda线输出
IIC_SDA_Mode_Out( pSoftIIC); //sda线输出
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 0);//STOP:when CLK is high DATA change form low to high
delay_us(4);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t IIC_Wait_Ack(const IIC_SOFT_TypeDef *pSoftIIC)
{
uint8_t ucErrTime=0;
// SDA_IN(); //SDA设置为输入
IIC_SDA_Mode_In( pSoftIIC); //SDA设置为输入
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);delay_us(1);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);delay_us(1);
while(IIC_READ_SDA_Pin(pSoftIIC))
// while( READ_SDA() )
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop(pSoftIIC);
return 1;
}
}
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(const IIC_SOFT_TypeDef *pSoftIIC)
{
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
// SDA_OUT();//sda线输出
IIC_SDA_Mode_Out( pSoftIIC); //sda线输出
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 0);
delay_us(2);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
delay_us(2);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
}
//不产生ACK应答
void IIC_NAck(const IIC_SOFT_TypeDef *pSoftIIC)
{
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
// SDA_OUT();//sda线输出
IIC_SDA_Mode_Out( pSoftIIC); //sda线输出
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, 1);
delay_us(2);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
delay_us(2);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(const IIC_SOFT_TypeDef *pSoftIIC,uint8_t txd)
{
uint8_t t;
// SDA_OUT();//sda线输出
IIC_SDA_Mode_Out( pSoftIIC); //sda线输出
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
// IIC_SDA=(txd&0x80)>>7;
// IIC_SDA( (txd&0x80)>>7);
GPIO_WRITE_PIN(pSoftIIC->sda_port, pSoftIIC->sda_pin, (txd&0x80)>>7 );
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
delay_us(2);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
delay_us(2);
}
}
uint8_t IIC_Read_Byte( const IIC_SOFT_TypeDef *pSoftIIC)
{
unsigned char i,receive=0;
// SDA_IN(); //SDA设置为输入
IIC_SDA_Mode_In( pSoftIIC); //SDA设置为输入
for(i=0;i<8;i++ )
{
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 0);
delay_us(2);
GPIO_WRITE_PIN(pSoftIIC->scl_port, pSoftIIC->scl_pin, 1);
receive<<=1;
if(IIC_READ_SDA_Pin(pSoftIIC))receive++;
// if( READ_SDA() )receive++;
delay_us(1);
}
return receive;
}
uint8_t IIC_Write_Test(const IIC_SOFT_TypeDef *pSoftIIC , uint8_t reg_addr, uint8_t *buf, uint8_t len)
{
IIC_Start(pSoftIIC);
IIC_Send_Byte(pSoftIIC,pSoftIIC->addr & 0xFE);
if(IIC_Wait_Ack(pSoftIIC)) { IIC_Stop(pSoftIIC); return -1;};
IIC_Send_Byte(pSoftIIC,reg_addr);
if(IIC_Wait_Ack(pSoftIIC)) { IIC_Stop(pSoftIIC); return -1;};
IIC_Send_Byte(pSoftIIC,*buf);
if(IIC_Wait_Ack(pSoftIIC)) { IIC_Stop(pSoftIIC); return -1;};
IIC_Stop(pSoftIIC);//产生一个停止条件
// HAL_Delay( 200 );
// delay_us(6000);
return 0;
}
// 寄存器地址 8位置
uint8_t IIC_Read_Test(const IIC_SOFT_TypeDef *pSoftIIC, uint8_t reg_addr, uint8_t *buf, uint8_t len)
{
uint8_t temp=0;
IIC_Start(pSoftIIC);
IIC_Send_Byte(pSoftIIC,pSoftIIC->addr & 0xFE);
if(IIC_Wait_Ack(pSoftIIC)) { IIC_Stop(pSoftIIC); return -1;};
IIC_Send_Byte(pSoftIIC,reg_addr);
if(IIC_Wait_Ack(pSoftIIC)) { IIC_Stop(pSoftIIC); return -1;};
IIC_Start(pSoftIIC);
IIC_Send_Byte(pSoftIIC,pSoftIIC->addr |0x01); //进入接收模式
if(IIC_Wait_Ack(pSoftIIC)) { IIC_Stop(pSoftIIC); return -1;};
for(int i=0; i<len-1; i++)
{
*buf=IIC_Read_Byte(pSoftIIC);
IIC_Ack(pSoftIIC); // 应答
buf++;
}
*buf = IIC_Read_Byte(pSoftIIC);// 读非应答
IIC_NAck(pSoftIIC);
// if(IIC_Wait_Ack()) { IIC_Stop(); return -1;};
IIC_Stop(pSoftIIC);
return 0;
}
在eeprom使用过程测试部分代码
HAL_I2C_MspDeInit( &hi2c1 );
uint8_t readout[256] = {0};
uint8_t writein[1] = {0xFB};
IIC_SOFT_TypeDef sIIC = {
GPIOB, GPIO_PIN_6, GPIOB, GPIO_PIN_7,
7,0xA0,1,
};
IIC_Init( &sIIC );
IIC_Write_Test( &sIIC,0xA0, 0, writein, 1 );
HAL_Delay( 200 );
IIC_Read_Test(&sIIC, 0xA0, 0, readout, 1 );
log_i("iic test %02X .... ", *readout);