历史上的今天
今天是:2025年07月23日(星期三)
2021年07月23日 | STM32CubeMX | 29-使用硬件I2C读取甲醛传感器SGP30
2021-07-23 来源:eefocus
本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件I2C外设读取环境光强度传感器数据(BH1750)。
【STM32Cube_09】重定向printf函数到串口输出的多种方法。
https://github.com/Mculover666/HAL_Driver_Lib
参考Sensirion_Gas_Sensors_SGP30_Datasheet_EN.PDF.pdf进行编程。
宏定义SGP30器件地址
先来编写sgp30.h头文件,SGP30的器件地址在数据手册中已给出:
注意数据手册中给出了8位数据,只有低7位用作地址,结合原理图,可以定义如下:
#define SGP30_ADDR 0x58
#define SGP30_ADDR_WRITE SGP30_ADDR<<1 //0xb0
#define SGP30_ADDR_READ (SGP30_ADDR<<1)+1 //0xb1
传感器数据封装
typedef struct sgp30_data_st {
uint16_t co2;
uint16_t tvoc;
}sgp30_data_t;
枚举SHT30命令列表
参考数据手册,在sgp30.h头文件中给出如下枚举定义:
typedef enum sgp30_cmd_en {
/* 初始化空气质量测量 */
INIT_AIR_QUALITY = 0x2003,
/* 开始空气质量测量 */
MEASURE_AIR_QUALITY = 0x2008
} sgp30_cmd_t;
发送命令函数
/**
* @brief 向SGP30发送一条指令(16bit)
* @param cmd SGP30指令
* @retval 成功返回HAL_OK
*/
static uint8_t sgp30_send_cmd(sgp30_cmd_t cmd)
{
uint8_t cmd_buffer[2];
cmd_buffer[0] = cmd >> 8;
cmd_buffer[1] = cmd;
return HAL_I2C_Master_Transmit(&hi2c1, SGP30_ADDR_WRITE, (uint8_t*) cmd_buffer, 2, 0xFFFF);
}
复位函数
/**
* @brief 软复位SGP30
* @param none
* @retval 成功返回HAL_OK
*/
static int sgp30_soft_reset(void)
{
uint8_t cmd = 0x06;
return HAL_I2C_Master_Transmit(&hi2c1, 0x00, &cmd, 1, 0xFFFF);
}
SGP30初始化函数
/**
* @brief 初始化SGP30空气质量测量模式
* @param none
* @retval 成功返回0,失败返回-1
*/
int sgp30_init(void)
{
int status;
status = sgp30_soft_reset();
if (status != HAL_OK) {
return -1;
}
HAL_Delay(100);
status = sgp30_send_cmd(INIT_AIR_QUALITY);
HAL_Delay(100);
return status == 0 ? 0 : -1;
}
发送一次测量开始命令
/**
* @brief 初始化SGP30空气质量测量模式
* @param none
* @retval 成功返回HAL_OK
*/
static int sgp30_start(void)
{
return sgp30_send_cmd(MEASURE_AIR_QUALITY);
}
从SGP30读取一次数据并校验解析
在数据手册中可知,SGP30分别在co2浓度之后和TVOC浓度数据之后发送了8-CRC校验码,确保了数据可靠性。
关于CRC校验请参考我的另一篇博客:如何通俗的理解CRC校验并用C语言实现。
SGP30校验的参数已经在数据手册中给出:

编写CRC-8校验函数如下:
#define CRC8_POLYNOMIAL 0x31
uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
uint8_t remainder; //余数
uint8_t i = 0, j = 0; //循环变量
/* 初始化 */
remainder = initial_value;
for(j = 0; j < 2;j++)
{
remainder ^= message[j];
/* 从最高位开始依次计算 */
for (i = 0; i < 8; i++)
{
if (remainder & 0x80)
{
remainder = (remainder << 1)^CRC8_POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
}
/* 返回计算的CRC码 */
return remainder;
}
接下来编写读取并校验数据的函数:
/**
* @brief 读取一次空气质量数据
* @param none
* @retval 成功返回0,失败返回-1
*/
int spg30_read(void)
{
int status;
uint8_t recv_buffer[6]={0};
/* 启动测量 */
status = sgp30_start();
if (status != 0) {
printf("sgp30 start failrn");
return -1;
}
HAL_Delay(100);
/* 读取测量数据 */
status = HAL_I2C_Master_Receive(&hi2c1, SGP30_ADDR_READ, (uint8_t*)recv_buffer, 6, 0xFFFF);
if (status != HAL_OK) {
printf("I2C Master Receive failrn");
return -1;
}
/* 校验接收的测量数据 */
if (CheckCrc8(&recv_buffer[0], 0xFF) != recv_buffer[2]) {
printf("co2 recv data crc check failrn");
return -1;
}
if (CheckCrc8(&recv_buffer[3], 0xFF) != recv_buffer[5]) {
printf("tvoc recv data crc check failrn");
return -1;
}
/* 转换测量数据 */
sgp30_data.co2 = recv_buffer[0] << 8 | recv_buffer[1];
sgp30_data.tvoc = recv_buffer[3] << 8 | recv_buffer[4];
return 0;
}
5. 测试SHT30驱动程序
在main.c中包含头文件:
#include #include "sgp30.h" 在main函数中对该驱动进行测试,修改main函数: 登录后复制 int main(void) { HAL_Init(); SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ printf("SGP30 Test By mculover666rn"); if (-1 == sgp30_init()) { printf("sgp30 init failrn"); /* 因为是裸机,所以直接进入死机 */ while(1); } printf("sgp30 init successrn"); /* USER CODE END 2 */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if( -1 == spg30_read()) { printf("sgp30 read failrn"); } else { printf("sgp30 read success, co2:%4d ppm, tvoc:%4d ppdrn", sgp30_data.co2, sgp30_data.tvoc); } HAL_Delay(2000); } /* USER CODE END 3 */ } 测试结果如图:
史海拾趣
|
有人用TMS320VC5509A的么?请教:ioport的问题. 5509A的IO空间0x0000~0xffff. MOV #1, port(#01h) 此语句会改写中断标志寄存器IFR0么? 如果我想使用 IO空间 0000~0007的地址去 扩展地址线ADDR14,ADDR15,ADDR16.可行么? … 查看全部问答> |
|
提供按摩器,按摩棒,震动器,震动棒方案开发及IC徐工:13713904933 QQ:104018792(技术交流) 我司是享誉电子产业界的义隆电子<EMC>的直接代理商,并专业从事电子产品研发,单片机开发,单片机解密。公司拥有数十名资 ...… 查看全部问答> |
|
谢谢大家对我的支持,2011我会继续努力的。 现在我在做统计的工作,在回我贴的朋友里并明确告诉我投我的,我做了下统计,把名单列出来,大家看看还有没有遗漏?有出路的话请抓紧时间跟帖告诉我,你应该排在的顺序,那个压缩包的密码我先不公布,等 ...… 查看全部问答> |
|
如何在ccs2.2里面实时打出一些程序运行的信息?或将信息保存? 如何在ccs2.2里面实时打出一些程序运行的信息?比如某个变量的值。或者如何将一个变量的一系列值保存到文件中阅读?谢谢! … 查看全部问答> |
|
最近比较忙,过段时间就要考期末了,所以半夜出来发代码,后面可能没时间了,发完代码睡觉,呵呵。 相信用过1602的人都知道,1602的相应操作,本文只想讲下4位数据的1602的驱动模式。 其实,4位的1602和8位的区别主要 ...… 查看全部问答> |
|
#include typedef unsigned char uchar ; typedef unsigned int uint; uchar table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar tt,dat; sbit button = P3^7; void delay_50us(uint t) { &nbs ...… 查看全部问答> |




