目錄
一、硬件連接
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