简述一下调试的心酸历程,刚开始用的32硬件IIC,眼看马上就要读出数据了,可是硬件iic在读取CCS811的0x02据寄存器的时候不能给每一个接收到的字节产生应答,导致CCS811只发出一个正确的字节,又不得不改为模拟iic,有关芯片的知识就不在这多介绍了,具体流程如图
步骤1:读CCS811的ID
` void CCS_Read_ID(void){
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte(0x20);
IIC_Wait_Ack();
IIC_Stop();
IIC_Start();
IIC_Send_Byte(0xb5);
IIC_Wait_Ack();
RcvByte();
IIC_NAck();
IIC_Stop();
}`
步骤2:从boot模式到应用模式
void CCS_HT_Mode(void){
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte(0xF4);
IIC_Wait_Ack();
IIC_Stop();
}
步骤3:读0x00寄存器看是否进入了应用模式
void CCS_ChkWorkMode(void ){
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte( 0x00);
IIC_Wait_Ack();
IIC_Stop();
IIC_Start();
IIC_Send_Byte(0xb5);
IIC_Wait_Ack();
RcvByte();
IIC_NAck();
IIC_Stop();
}
步骤4:设置寄存器0x01,设定读取间隔时间
void CCS_SetReadMode(void){
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte( 0x01);
IIC_Wait_Ack();
IIC_Send_Byte( 0x10);
IIC_Wait_Ack();
IIC_Stop();
//设置完成后并读取0x01,检测是否设置成功
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte( 0x01);
IIC_Wait_Ack();
IIC_Stop();
IIC_Start();
IIC_Send_Byte(0xb5);
IIC_Wait_Ack();
RcvByte();
IIC_NAck();
IIC_Stop();
}
步骤5:读取co2浓度,8字节0x02寄存器,我这里只读取了两个字节
void read_CO2ppm(void){
uint8_t TEMP =0;
uint8_t co2H = 0;
uint8_t co2L = 0;
uint16_t co2 = 0;
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte( 0x00);
IIC_Wait_Ack();
IIC_Stop();
IIC_Start();
IIC_Send_Byte(0xb5);
IIC_Wait_Ack();
TEMP = RcvByte();
IIC_NAck();
IIC_Stop();
if(TEMP&(1<<3)){//先读00寄存器,新数据是否准备好
IIC_Start();
IIC_Send_Byte( 0xB4);
IIC_Wait_Ack();
IIC_Send_Byte( 0x02);
IIC_Wait_Ack();
IIC_Stop();
IIC_Start();
IIC_Send_Byte(0xb5);
IIC_Wait_Ack();
co2H = RcvByte();
IIC_Ack();
co2L = RcvByte();
IIC_NAck();
IIC_Stop();
co2 = (co2H<<8)+co2L;
printf("CO2ppm = %d\r\n",co2);
}
}
~~
~~
#define SCLL1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
#define SCLH1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
#define SDAL1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
#define SDAH1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
#define SDA_STATE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)
void CCS_IO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStructure.Pin = GPIO_PIN_6| GPIO_PIN_7;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO1_Init(GPIOB, &GPIO_InitStructure);
SDAH1;
SCLH1;
}
void SDA_IN(void){
GPIO_InitTypeDef GPIO_InitStruct;
// GPIO_InitStructure.Pin = GPIO_PIN_7;
// GPIO_InitStructure.Mode = GPIO_MODE_INPUT ;
// GPIO1_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void SDA_OUT(void){
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD ;
GPIO1_Init(GPIOB, &GPIO_InitStructure);
}
//SYSCLK:系统时钟
void delay_init(u8 SYSCLK)
{
SysTick->CTRL&=~(1<<2); //SYSTICK使用外部时钟源
fac_us=SYSCLK/8; //不论是否使用OS,fac_us都需要使用
}
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//接收一个字节
u8 RcvByte(void)
{
u8 i,retc=0;
SDAH1;
SDA_IN();
for(i=0; i<8; i++)
{
SCLL1;
delay_us(50);
SCLH1;
delay_us(50);
retc = retc << 1;
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7))
{
retc++;
}
}
SCLL1;
delay_us(50);
return retc;
}
void IIC_Start(void)
{
// SDA_OUT(); //sda线输出
SDAH1;
SCLH1;
delay_us(50);
SDAL1;//START:when CLK is high,DATA change form high to low
delay_us(50);
SCLL1;//钳住I2C总线,准备发送或接收数据
}
void IIC_Stop(void)
{
/* 结束条件: SCL高, SDA低->高 */
SDA_OUT();//sda线输出
SCLL1;
SDAL1;//STOP:when CLK is high DATA change form low to high
delay_us(50);
SCLH1;
delay_us(50);
SDAH1;//发送I2C总线结束信号
delay_us(50);
}
u8 IIC_Wait_Ack(void)
{
u16 ucErrTime=0;
SDAH1;delay_us(25);
SDA_IN();
SCLH1;delay_us(25);
//SDA设置为输入
while(SDA_STATE)
{
ucErrTime++;
if(ucErrTime>60000)
{
IIC_Stop();
return 1;
}
}
delay_us(30);
SCLL1;//时钟输出0
return 0;
}
void IIC_NAck(void)
{
SCLL1;
SDA_OUT();
SDAH1;
delay_us(50);
SCLH1;
delay_us(50);
SCLL1;
}
void IIC_Ack(void)
{
SCLL1;
SDA_OUT();
SDAL1;
delay_us(2);
SCLH1;
delay_us(2);
SCLL1;
}
void IIC_Send_Byte(u8 txd)
{
u8 i;
SDA_OUT();
SCLL1;//拉低时钟开始数据传输
for(i=0; i<8; i++)
{
if((txd << i) & 0x80)
{
SDAH1;
}
else
{
SDAL1;
}
delay_us(50);
SCLH1;
delay_us(50);
SCLL1;
}
delay_us(20);
}
附上硬件iic的代码,读取第一个字节设置应答之后也是NACK,导致第二个字节是错误的,就算调通也不太敢用,太多的while随便出一个fault就会卡死。
`
void I2C1_Write_Bety(uint8_t data){
uint32_t rData = 0;
while(I2C1->SR2 & BUS_BUSY);//总线忙,等待?
I2C1->CR1 |= 1 << 8;//置为,产生起始条件
while(!(I2C1->SR1 & (1 << 0)));//SB位置位说明已产生启示条件
I2C1->DR = data;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 1))){
};//等待应答
rData = I2C1->SR2;
}
uint32_t I2C1_Read_Bety(void){
uint32_t _I2C1Data = 0;
while(!(I2C1->SR1 & (1 << 6)));//判断RxNE是否置位
_I2C1Data = I2C1->DR ;
delay_us(10);
I2C2->CR1 &= ~(1<< 10);//清除ACK位
delay_us(10);
I2C1->CR1 |= 1 << 9;//产生停止条件
return _I2C1Data;
}
void CCS811_Init(void){
uint8_t uStaus = 0;
//读器件ID WRITE_CCS_ADDRESS
I2C1_Write_Bety(0xA0);
I2C1->DR = CCS_HW_ID_REG;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
I2C1_Write_Bety(0xB5);
if(CCS811_ID != I2C1_Read_Bety()){//读81
return ;
}
//启动模式
delay_us(200);
I2C1_Write_Bety(0xB4);
I2C1->DR = CCS_APPSTART_REG;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
//监测是否进入测量模式
I2C1_Write_Bety(0xB4);
I2C1->DR = CCS_STAUS_REG;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
I2C1_Write_Bety(0XB5);
uStaus = I2C1_Read_Bety();
//printf("3");
if(!(uStaus&(1<<7))){//是否进入应用模式
return ;
}
delay_us(1000000);
I2C1_Write_Bety(0xB4);
I2C1->DR = CCS_MEASMODE_REG;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->DR = 0x10;
while(!(I2C1->SR1 & (1 << 7)));
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
I2C1_Write_Bety(0xB4);
I2C1->DR = CCS_MEASMODE_REG;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
I2C1_Write_Bety(0XB5);
I2C1_Read_Bety();
printf("CCS811Init.OK............");
gPublicTimer.isReadCo2Time = T_1S;
}
//读取
uint8_t RxDATA = 0;
I2C1_Write_Bety(0xB4);
I2C1->DR = CCS_STAUS_REG;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
I2C1_Write_Bety(0XB5);
RxDATA = I2C1_Read_Bety();
uint8_t co2ppm[2] ={0};
if(RxDATA&(1<<3)){
I2C1_Write_Bety(0xB4);
I2C1->DR = CCS_ALGRES_DATA;//将从机地址写入数据寄存器
while(!(I2C1->SR1 & (1 << 7)));//发送完成
I2C1->CR1 |= 1 << 9;//置为,产生停止条件
I2C1_Write_Bety(0XB5);
while(!(I2C1->SR1 & (1 << 6)));//判断RxNE是否置位
co2ppm[0] = I2C1->DR ;
delay_us(150);
I2C2->CR1 |=(1<< 10);
while(!(I2C1->SR1 & (1 << 6)));//判断RxNE是否置位
co2ppm[1] = I2C1->DR ;
I2C2->CR1 &=~(1<< 10);
I2C1->CR1 |= 1 << 9;//产生停止条件
printf("co2ppm = %x\r\n",(co2ppm[0]<<8)+co2ppm[1]);
`