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