CCS811二氧化碳和TVOC傳感器 HAL庫 模擬IIC調試成功 適用於CubeMX CubeIDE

目錄

一、硬件連接

二、模擬IIC

頭文件

C文件

三、CCS811過程

CCS811工作流程(官方文檔)

1. [Boot Mode] 讀取硬件ID

2. [Boot Mode] 讀取狀態值

3. [Boot Mode] 切換到APP模式

4. [APP Mode] 讀取狀態值

5. [APP Mode] 設置測量週期

6. [APP Mode] 讀取傳感器值

CCS811代碼(改編自文獻2)

頭文件

參考文獻

一、硬件連接

VCC - 3.3V

SCL和SDA接IIC

WAK接地(也可以由單片機引腳控制)

 

二、模擬IIC

頭文件

#ifndef __MYIIC_H
#define __MYIIC_H
#include "main.h"

void IIC_Init(void);                		 
void IIC_Start(void);				
void IIC_Stop(void);	  			
void IIC_Send_Byte(uint8_t txd);			
uint8_t IIC_Read_Byte(unsigned char ack);
uint8_t IIC_Wait_Ack(void); 				
void IIC_Ack(void);					
void IIC_NAck(void);				

#endif

C文件

#include "my_iic.h"
#include "delay.h"

#define HT_SDA GPIO_PIN_7 //B7
#define HT_SCK GPIO_PIN_6 //B6

static void SDA_IN(){
 GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pins : PAPin PAPin */
  GPIO_InitStruct.Pin = HT_SDA;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

static void SDA_OUT(){
 GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = HT_SDA;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void SDA_HIGH(){
  HAL_GPIO_WritePin(GPIOB, HT_SDA, GPIO_PIN_SET);
}

void SDA_LOW(){
  HAL_GPIO_WritePin(GPIOB, HT_SDA, GPIO_PIN_RESET);
}

void SCL_HIGH(){
  HAL_GPIO_WritePin(GPIOB, HT_SCK, GPIO_PIN_SET);
}

void SCL_LOW(){
  HAL_GPIO_WritePin(GPIOB, HT_SCK, GPIO_PIN_RESET);
}

GPIO_PinState read_sdaval(){
   return HAL_GPIO_ReadPin(GPIOB,HT_SDA);
}

void IIC_Start(void)
{
	SDA_OUT(); 

	SDA_HIGH();
	SCL_HIGH();
	delay_us(5);
	SDA_LOW();//START:when CLK is high,DATA change form high to low
	delay_us(5);
	SCL_LOW();
	delay_us(5);
}	  

void IIC_Stop(void)
{
	SDA_OUT();

	SCL_LOW();
	SDA_LOW();//STOP:when CLK is high DATA change form low to high
 	delay_us(5);
 	SCL_HIGH();
	SDA_HIGH();
	delay_us(5);							   	
}

uint8_t IIC_Wait_Ack(void)
{
	uint8_t ucErrTime=0;
	SDA_IN(); //
	SDA_HIGH();delay_us(1);
	SCL_HIGH();delay_us(1);
	while(read_sdaval())
	{
		ucErrTime++;
		delay_us(1);
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	SCL_LOW();
	return 0;  
} 

void IIC_Ack(void)
{
	SCL_LOW();
	SDA_OUT();

	SDA_LOW();
	delay_us(20);
	SCL_HIGH();
	delay_us(5);
	SCL_LOW();
}

void IIC_NAck(void)
{
	SCL_LOW();
	SDA_OUT();

	SDA_HIGH();
	delay_us(5);
	SCL_HIGH();
	delay_us(5);
	SCL_LOW();
}					 				     

void IIC_Send_Byte(uint8_t txd)
{                        
	uint8_t t;
	SDA_OUT();

	SCL_LOW();
    for(t=0;t<8;t++)
    {              
		if((txd&0x80)>>7)
			SDA_HIGH();
		else
			SDA_LOW();
		txd<<=1; 	  
		delay_us(50);
		SCL_HIGH();
		delay_us(50); 
		SCL_LOW();
		delay_us(50); 
		
    }	 

} 	    

uint8_t IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();
    for(i=0;i<8;i++ )
	{
    	SCL_LOW();
        delay_us(50);
        SCL_HIGH();
        receive<<=1;
        delay_us(25);
        if(read_sdaval())
        {
        	receive++;
        }
		delay_us(25);
    }					 
    if (!ack)
        IIC_NAck();//nACK
    else
        IIC_Ack(); //ACK
    return receive;
}

三、CCS811過程

CCS811工作流程(官方文檔)

1. [Boot Mode] 讀取硬件ID

判斷是不是0x81 (129),如果不是則IIC邏輯問題或者電平問題(供電電壓在1.8V-3.6V,以及對應匹配電阻)

2. [Boot Mode] 讀取狀態值

如果APP_VALID值不爲1,則內部MCU的程序不對,需要燒錄(一般不會出現這個問題,也有人遇到過,見

CCS811二氧化碳傳感器基於Arduino的升級固件(刷機)教程

3. [Boot Mode] 切換到APP模式

從boot模式切換到APP模式纔可以進行測量。只需在地址0xF4寄存器內寫1Byte操作。

4. [APP Mode] 讀取狀態值

通過讀取狀態值,判斷切換到APP模式是否成功。狀態位爲FW_MODE

5. [APP Mode] 設置測量週期

在MEAS_MODE寫入參數,例如1秒測一次寫0x10。注意不同週期切換時間可能需要數十秒的時間。

6. [APP Mode] 讀取傳感器值

從地址0x02讀取8Byte數據,其中測量結果對應Byte位見下表

CCS811代碼(改編自文獻2)

頭文件

#ifndef CCS811_h
#define CCS811_h

#include "delay.h"
#include "my_iic.h"

#define boolean uint8_t
#define byte 	uint8_t

typedef struct {
	uint16_t eco2;
	uint16_t tvoc;
	uint8_t status;
	uint8_t error_id;
	uint8_t hw_id;
	uint16_t raw_data;
} ccs811_measurement_t;

#define CCSADDR				0x5A<<1 // when I2C_ADDR pin is LOW
// #define CCSADDR					0x5B<<1 when I2C_ADDR pin is HIGH
#define READ            	0xB5  
#define WRIT            	0xB4   
// Registers for CCS811
#define STATUS          	0x00
#define MEAS_MODE       	0x01
#define ALG_RESULT_DATA 	0x02
#define RAW_DATA			0x03
#define ENV_DATA        	0x05
#define NTC        			0x06
#define THRESHOLDS      	0x10
#define BASELINE      		0x11
#define HW_ID           	0x20
#define HW_Version 			0x21
#define FW_Boot_Version		0x23
#define FW_App_Version  	0x24
#define ERROR_ID        	0xE0
#define SW_RESET        	0xFF

#define MODE0 				0x00    
#define MODE1 				0x10	
#define MODE2 				0x20	
#define MODE3 				0x30	
#define MODE4 				0x40	

extern ccs811_measurement_t CCS;

void readStatus(void);
void readHW_ID(void);
void readErrorID(void);
void readALG_RESULT_DATA(uint8_t *data);
void app_Start(void);
	
int readTVOC(void);
int readCO2(void);
void getData(void);
void compensate(float t, float rh);
void setMode(uint8_t mode);

void reset(void);
void sleep(void);
#endif

C文件

#include "CCS811.h"

ccs811_measurement_t CCS;

#define nWAKE_HIGN() LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_5)
#define nWAKE_LOW() LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_5)
#define nINT_LOW() LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_4)
#define nINT_HIGN() LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_4)

//查看是否有新數據 返回值 1:有
//						  0:沒有
uint8_t CheckData_Ready(void)
{
		readStatus();
	if((CCS.status&8)==1) return 1;
	else return 0;
}

//讀取一個bit數據
uint8_t CCS811_ReadOneByte(uint8_t ReadAddr)
{
	uint8_t temp=0;
		nWAKE_LOW();
		delay_us(50);//必須等待50us
		IIC_Start();  						
		//addr接地,7位地址爲0x5A,讀地址爲AB,寫地址AA
		IIC_Send_Byte(WRIT);	   //發送寫命令
		if(IIC_Wait_Ack()){IIC_Stop(); return 255;}
		//else nINT=0;
		IIC_Send_Byte(ReadAddr);
		if(IIC_Wait_Ack()){IIC_Stop(); return 255;}
		//if(!IIC_Wait_Ack()){nINT=1;delay_ms(200);nINT=0;delay_ms(200);}
		delay_ms(1);
		IIC_Start();
		IIC_Send_Byte(READ);
		if(IIC_Wait_Ack()){IIC_Stop(); return 255;}
		temp=IIC_Read_Byte(0); 

		IIC_Stop();
		nWAKE_HIGN();
		delay_us(50);
	return temp;
}

void CCS811_WriteOneByte(uint8_t RegAddr,uint8_t DataToWrite)
{
	nWAKE_LOW();
	delay_us(50);
	IIC_Start(); 
	IIC_Send_Byte(WRIT);	   //發送寫命令
	IIC_Wait_Ack();
	IIC_Send_Byte(RegAddr);
	IIC_Wait_Ack();
	IIC_Send_Byte(DataToWrite);
	IIC_Wait_Ack();
	IIC_Stop();
	nWAKE_HIGN();
}
//測量數據前必須開啓
void app_Start(void)
{
	nWAKE_LOW();
	delay_us(50);
	IIC_Start(); 
	IIC_Send_Byte(WRIT);	   //發送寫命令
	if(IIC_Wait_Ack())
	{
	IIC_Stop();return;
	}
	IIC_Send_Byte(0xF4);
	delay_us(180);			//必須等待180us以上才能夠收到應答
	if(IIC_Wait_Ack()){IIC_Stop();return;}
	IIC_Stop();
	nWAKE_HIGN();

}

void readStatus(void)
{
  CCS.status=CCS811_ReadOneByte(STATUS);
}


void readHW_ID(void)
{
	CCS.hw_id=CCS811_ReadOneByte(HW_ID);
}


void readErrorID(void)
{
	CCS.error_id=CCS811_ReadOneByte(ERROR_ID);
}

void setMode(uint8_t mode)
{
//	nWAKE=0;
//	delay_us(50);
	CCS811_WriteOneByte(MEAS_MODE,mode);
//	nWAKE=1;
}
	

//一次讀8bit數據,前2bit爲eco2數據,接着2bit爲tvoc數據
//當status第五位爲1時有新數據。
void getData(void)
{
	uint8_t i,buffer[8];
	
	nWAKE_LOW();
	delay_us(50);
	IIC_Start(); 
	IIC_Send_Byte(WRIT);	   //發送寫命令
	IIC_Wait_Ack();
	IIC_Send_Byte(ALG_RESULT_DATA);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_us(50);
	IIC_Start(); 
	IIC_Send_Byte(READ);	   //發送寫命令
	IIC_Wait_Ack();
	for(i=0;i<8-1;i++)
	buffer[i]=IIC_Read_Byte(1);

	buffer[i]=IIC_Read_Byte(0);
	IIC_Stop();
	nWAKE_HIGN();
	
	CCS.eco2 = ((uint16_t)buffer[0] << 8) + buffer[1];
	CCS.tvoc = ((uint16_t)buffer[2] << 8) + buffer[3];
	CCS.status=buffer[4];
	
}
//睡眠模式
void sleep(void)
{
	CCS811_WriteOneByte(MEAS_MODE,0x00000000);
}

//軟重置,回到boot模式
void reset(void)
{
	uint8_t ResetData[4]={0X11,0XE5,0X72,0X8A};
	nWAKE_LOW();
	delay_us(50);
	IIC_Start(); 
	IIC_Send_Byte(WRIT);	   //發送寫命令
	IIC_Wait_Ack();
	IIC_Send_Byte(SW_RESET);
	IIC_Wait_Ack();
	IIC_Send_Byte(0X11);
	IIC_Wait_Ack();
	IIC_Send_Byte(0XE5);
	IIC_Wait_Ack();
	IIC_Send_Byte(0X72);
	IIC_Wait_Ack();
	IIC_Send_Byte(0X8A);
	IIC_Wait_Ack();
	IIC_Stop();
	nWAKE_HIGN();
}

main.c

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_Delay(1000);

  readHW_ID();
  printf("id:%d\r\n",CCS.hw_id);

  readStatus();
  printf("sta0:%d\r\n",CCS.status);

  app_Start();

  readStatus();
  printf("sta1:%d\r\n",CCS.status);

  setMode(0x10);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  HAL_Delay(998);
	  getData();
	  printf("eco2:%d",CCS.eco2);
	  printf("tvoc:%d\r\n",CCS.tvoc);
	  readStatus();
	  printf("sta:%d\r\n",CCS.status);
  }
  /* USER CODE END 3 */
}

參考文獻

1.二氧化碳傳感器CCS811簡單的測試驅動_基於Arduino

2.ccs811調試指南

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章