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

CCS811传感器采集CO2浓度

简述一下调试的心酸历程,刚开始用的32硬件IIC,眼看马上就要读出数据了,可是硬件iic在读取CCS811的0x02据寄存器的时候不能给每一个接收到的字节产生应答,导致CCS811只发出一个正确的字节,又不得不改为模拟iic,有关芯片的知识就不在这多介绍了,具体流程如图
基于STM32的I2C调试CO2传感器CCS811_第1张图片

详细步骤

步骤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);
 }

基于STM32的I2C调试CO2传感器CCS811_第2张图片
基于STM32的I2C调试CO2传感器CCS811_第3张图片

  • 附上硬件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]);

`

你可能感兴趣的:(传感器,CCS811,STM32模拟iic,CO2传感器)