CCS811二氧化碳和TVOC传感器 HAL库 模拟IIC调试成功 适用于CubeMX CubeIDE
程序员文章站
2024-02-27 12:27:51
...
目录
一、硬件连接
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;
}
void readALG_RESULT_DATA(uint8_t *data)
{
nWAKE_LOW();
delay_us(50);
Single_ReadIIC(WRIT,ALG_RESULT_DATA,data,8);
nWAKE_HIGN();
}
//一次读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