基于STM32的I2C调试CO2传感器CCS811

CCS811传感器采集CO2浓度

简述一下调试的心酸历程,刚开始用的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 lowdelay_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 highdelay_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;//时钟输出0return 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()){//读81return ;
}//启动模式
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]);

`


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部