最近在调试一款,IIC芯片,用的是软件模拟的IIC,发现芯片的配置寄存器,写入的内容是有响应的,数据线(SDA)ack为零,一般这样是认为写入成功的。然后去读那个配置寄存器,发现这个寄存器返回的内容一直在变化,从机也一直是有响应的,ack也一直是零。因为这个软件IIC驱动很多其他IIC芯片是成功,所以怀疑到了芯片有问题… 但芯片有问题的概率还是比较小的,后面用硬件IIC成功访问到了这个芯片,配置寄存器的内容,返回也是正确的,不会变来变去。
我使用的模拟IIC是快速模式,硬件IIC的标准模式和快速模式都成功驱动了这个芯片,所以就是发现我使用的模拟IIC延时不匹配这个芯片。IIC标准模式和快速模式的时间需求如下。
后续是将快速模拟的IIC换成了标准速度的模拟IIC驱动成功了芯片,如果需要IIC来驱动显示屏这种,或者需要快速模式的IIC,建议还是用硬件IIC,软件IIC就用标准速度就好了。下面附上,标准速度的模拟IIC驱动。stm32的。延时是参考别的大佬写的,要修改成,自己时钟主频大小,单位M。
#define CPU_FREQUENCY_MHZ 40
/**
* @brief delay_us
* @param uint32_t nTime us
* @retval None
*/
void delay_us(uint32_t nTime)
{
int old_val,new_val,val;
if(nTime > 900)
{
for(old_val = 0; old_val < nTime/900; old_val++)
{
delay_us(900);
}
nTime = nTime%900;
}
old_val = SysTick->VAL;
new_val = old_val - CPU_FREQUENCY_MHZ*nTime;
if(new_val >= 0)
{
do
{
val = SysTick->VAL;
}
while((val < old_val)&&(val >= new_val));
}
else
{
new_val +=CPU_FREQUENCY_MHZ*1000;
do
{
val = SysTick->VAL;
}
while((val <= old_val)||(val > new_val));
}
}
#include "bsp_SimulateI2C.h"
/*
*********************************************************************************************************
* function : bsp_SimuI2C_read_nBytes_ack api
* Description : read date
* Argument(s) : pSimuI2cPort: point to struct SimuI2cPortType, data: data to write
* Return(s) : ack
*********************************************************************************************************
*/
uint32_t bsp_SimuI2C_read_nBytes_ack(
struct SimuI2cPortType *pSimuI2cPort,
uint8_t device_addr,
uint8_t RegAddr,
uint8_t *pValue,
uint8_t Cnt)
{
uint32_t i, ack=0;
//write dev addr
bsp_SimuI2C_start(pSimuI2cPort);
bsp_SimuI2C_write_byte(pSimuI2cPort, (device_addr&0xFE));//chip-addr
ack = bsp_SimuI2C_ReadAck(pSimuI2cPort);//read ack
//write reg addr
bsp_SimuI2C_write_byte(pSimuI2cPort, RegAddr);//reg-addr
ack += bsp_SimuI2C_ReadAck(pSimuI2cPort);//read ack
//write dev addr
bsp_SimuI2C_start(pSimuI2cPort);//restart
bsp_SimuI2C_write_byte(pSimuI2cPort, (device_addr|0x01));//chip-addr
ack += bsp_SimuI2C_ReadAck(pSimuI2cPort);//read ack
//read N datas
for(i=0; i<Cnt; i++){
pValue[i] = bsp_SimuI2C_read_byte(pSimuI2cPort);//read data
if((Cnt-1) == i){
bsp_SimuI2C_SandNack(pSimuI2cPort);//send nack
}else{
bsp_SimuI2C_SandAck(pSimuI2cPort);//send ack
}
}
bsp_SimuI2C_stop(pSimuI2cPort);
return ack;
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_write_nBytes_noAck api
* Description : write date
* Argument(s) : pSimuI2cPort: point to struct SimuI2cPortType, data: data to write
* Return(s) : ack
*********************************************************************************************************
*/
uint32_t bsp_SimuI2C_write_nBytes_noAck(
struct SimuI2cPortType *pSimuI2cPort,
uint8_t device_addr,
uint8_t RegAddr,
uint8_t *pValue,
uint8_t Cnt)
{
uint32_t ack=0;
uint16_t i=0;
bsp_SimuI2C_start(pSimuI2cPort);
bsp_SimuI2C_write_byte(pSimuI2cPort, (device_addr&0xFE));//chip-addr
ack = bsp_SimuI2C_ReadAck(pSimuI2cPort);//read ack
bsp_SimuI2C_write_byte(pSimuI2cPort, RegAddr);//reg-addr
ack += bsp_SimuI2C_ReadAck(pSimuI2cPort);//read ack
//write N datas
for(i=0; i<Cnt; i++){
bsp_SimuI2C_write_byte(pSimuI2cPort, pValue[i]);//data
ack += bsp_SimuI2C_ReadAck(pSimuI2cPort);//read ack
}
bsp_SimuI2C_stop(pSimuI2cPort);
return ack;
}
/*
*********************************************************************************************************
* function : config_sda_in
* Description : config SDA GPIO for input
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : none
*********************************************************************************************************
*/
static void config_sda_in(struct SimuI2cPortType *pSimuI2cPort)
{
uint32_t position = 0x00;
while(((pSimuI2cPort->SDAPin) >> position) != 0){
position++;
}
position--;
pSimuI2cPort->SDAPort->MODER &= ~(3<<(position*2));
pSimuI2cPort->SDAPort->MODER |= 0<<(position*2);
}
/*
*********************************************************************************************************
* function : config_sda_out
* Description : config SDA GPIO for output
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : none
*********************************************************************************************************
*/
static void config_sda_out(struct SimuI2cPortType *pSimuI2cPort)
{
uint32_t position = 0x00;
while(((pSimuI2cPort->SDAPin) >> position) != 0){
position++;
}
position--;
pSimuI2cPort->SDAPort->MODER &= ~(3<<(position*2));
pSimuI2cPort->SDAPort->MODER |= 1<<(position*2);
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_start
* Description : generate a i2c start or restart
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : none
*********************************************************************************************************
*/
void bsp_SimuI2C_start(struct SimuI2cPortType *pSimuI2cPort)
{
//config sda pin output
config_sda_out(pSimuI2cPort);
SIMUI2C_SDA_SET;
SIMUI2C_SCL_SET;
delay_us(5);//确保开始前高保持4.7us以上
SIMUI2C_SDA_CLR;
delay_us(4);
SIMUI2C_SCL_CLR;
delay_us(4);
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_stop
* Description : generate a i2c stop
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : none
*********************************************************************************************************
*/
void bsp_SimuI2C_stop(struct SimuI2cPortType *pSimuI2cPort)
{
//config sda pin output
config_sda_out(pSimuI2cPort);
SIMUI2C_SCL_CLR;
SIMUI2C_SDA_CLR;
delay_us(4);
SIMUI2C_SCL_SET;
delay_us(4);//时钟线先拉高4us以上
SIMUI2C_SDA_SET;
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_SandAck
* Description : generate a i2c ack to slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : none
*********************************************************************************************************
*/
void bsp_SimuI2C_SandAck(struct SimuI2cPortType *pSimuI2cPort)
{
SIMUI2C_SCL_CLR;
config_sda_out(pSimuI2cPort);
SIMUI2C_SDA_CLR;
delay_us(2);
SIMUI2C_SCL_SET;
delay_us(2);
SIMUI2C_SCL_CLR;
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_SandNack
* Description : generate a i2c notack to slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : none
*********************************************************************************************************
*/
void bsp_SimuI2C_SandNack(struct SimuI2cPortType *pSimuI2cPort)
{
SIMUI2C_SCL_CLR;
config_sda_out(pSimuI2cPort);
SIMUI2C_SDA_SET;
delay_us(2);
SIMUI2C_SCL_SET;
delay_us(2);
SIMUI2C_SCL_CLR;
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_ReadAck
* Description : check i2c ack from slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : 0: ack, 1: nack
*********************************************************************************************************
*/
uint32_t bsp_SimuI2C_ReadAck(struct SimuI2cPortType *pSimuI2cPort)
{
uint8_t ack,ucErrTime=0;
config_sda_in(pSimuI2cPort);
SIMUI2C_SCL_SET;
delay_us(2);
while(SIMUI2C_SDA_IN)
{
if(++ucErrTime>250)
{
bsp_SimuI2C_stop(pSimuI2cPort);
return 1;
}
}
SIMUI2C_SCL_CLR;
return 0;
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_read_byte
* Description : read a byte from i2c slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s) : the read data
*********************************************************************************************************
*/
uint8_t bsp_SimuI2C_read_byte(struct SimuI2cPortType *pSimuI2cPort)
{
uint32_t i;
uint8_t data = 0;
config_sda_in(pSimuI2cPort);
for(i=0; i<8; i++) {
SIMUI2C_SCL_CLR;
delay_us(5);
SIMUI2C_SCL_SET;
//read data in
data<<=1;
if(SIMUI2C_SDA_IN) data |= 0x01;
delay_us(5);
}
return data;
}
/*
*********************************************************************************************************
* function : bsp_SimuI2C_write_byte
* Description : write a byte to i2c slave
* Argument(s) : pSimuI2cPort: point to struct SimuI2cPortType, data: data to write
* Return(s) : none
*********************************************************************************************************
*/
void bsp_SimuI2C_write_byte(struct SimuI2cPortType *pSimuI2cPort, uint8_t data)
{
uint32_t i;
config_sda_out(pSimuI2cPort);
SIMUI2C_SCL_CLR;
for(i=0; i<8; ++i) {
if(data & 0x80)
SIMUI2C_SDA_SET;
else
SIMUI2C_SDA_CLR;
data <<= 1;
delay_us(5);
SIMUI2C_SCL_SET;
delay_us(5);
SIMUI2C_SCL_CLR;
}
}
#ifndef __BSP_SIMULATE_I2C__
#define __BSP_SIMULATE_I2C__
#include "stm32l4xx_hal.h"
#include "stdio.h"
//Simulate I2C Port struct define
struct SimuI2cPortType{
GPIO_TypeDef *SCLPort;//GPIO PORT
uint32_t SCLPin; //GPIO PIN
GPIO_TypeDef *SDAPort;//GPIO PORT
uint32_t SDAPin; //GPIO PIN
};
//SCL output hardware operate
#define SIMUI2C_SCL_SET HAL_GPIO_WritePin(pSimuI2cPort->SCLPort, pSimuI2cPort->SCLPin, GPIO_PIN_SET)
#define SIMUI2C_SCL_CLR HAL_GPIO_WritePin(pSimuI2cPort->SCLPort, pSimuI2cPort->SCLPin, GPIO_PIN_RESET)
//SDA output hardware operate
#define SIMUI2C_SDA_SET HAL_GPIO_WritePin(pSimuI2cPort->SDAPort, pSimuI2cPort->SDAPin, GPIO_PIN_SET)
#define SIMUI2C_SDA_CLR HAL_GPIO_WritePin(pSimuI2cPort->SDAPort, pSimuI2cPort->SDAPin, GPIO_PIN_RESET)
//SDA input hardware operate
#define SIMUI2C_SDA_IN HAL_GPIO_ReadPin(pSimuI2cPort->SDAPort, pSimuI2cPort->SDAPin)
//simulate i2c APIS
void bsp_SimuI2C_start(struct SimuI2cPortType *pSimuI2cPort);
void bsp_SimuI2C_stop(struct SimuI2cPortType *pSimuI2cPort);
void bsp_SimuI2C_SandAck(struct SimuI2cPortType *pSimuI2cPort);
void bsp_SimuI2C_SandNack(struct SimuI2cPortType *pSimuI2cPort);
uint32_t bsp_SimuI2C_ReadAck(struct SimuI2cPortType *pSimuI2cPort);
uint8_t bsp_SimuI2C_read_byte(struct SimuI2cPortType *pSimuI2cPort);
void bsp_SimuI2C_write_byte(struct SimuI2cPortType *pSimuI2cPort, uint8_t data);
uint32_t bsp_SimuI2C_read_nBytes_ack(
struct SimuI2cPortType *pSimuI2cPort,
uint8_t device_addr,
uint8_t RegAddr,
uint8_t *pValue,
uint8_t Cnt);
uint32_t bsp_SimuI2C_write_nBytes_noAck(
struct SimuI2cPortType *pSimuI2cPort,
uint8_t device_addr,
uint8_t RegAddr,
uint8_t *pValue,
uint8_t Cnt);
#endif /* End of module include */
下面是应用举例。要定一个结构体,赋值GPIO口。
struct SimuI2cPortType xx_IIC_Ctrl_str;
void bsp_iic1_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//SCL-PB8, SDA-PB9
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//将IIC端口和引脚,赋值给定义的结构体,就可以调用读写API了。
xx_IIC_Ctrl_str.SCLPort = GPIOB;
xx_IIC_Ctrl_str.SCLPin = GPIO_PIN_8;
xx_IIC_Ctrl_str.SDAPort = GPIOB;
xx_IIC_Ctrl_str.SDAPin = GPIO_PIN_9;
}
//读n个字节举例
uint8_t rdata[2],ret = 0;
//0x20从机地址,0x08从机寄存器地址,读一个字节放到rdata[0],ret不是0,说明从机没有响应,读取失败。
ret = bsp_SimuI2C_read_nBytes_ack(&xx_IIC_Ctrl,0x20,0x08,rdata,1);
//写n个字节举例
uint8_t data[2] = {0x21,0x00};
ret = bsp_SimuI2C_write_nBytes_noAck(&xx_IIC_Ctrl,0x20,0x00,data,2);
//如果只是访问从机芯片,然后直接写数据,不需要访问从机的寄存器,可以自己找最底层读写、ack、开始、结束去组合使用。(根据芯片时序要求)