本文详细记录了如何使用它STM32CubeMX配置STM32L431RCT6的硬件I2C环境光强环境光强度传感器数据(BH1750)。
1. 准备工作
开发板
首先,我需要准备一个开发板。我在这里准备的是STM32L4的开发板(BearPi)。
SGP30传感器模块
SGP30是一种金属氧化物,单芯片上有多个传感元件,四个气体传感元件集成,空气质量输出信号完全校准,主要检测空气质量。TVOC(Total Volatile Organic Compounds,总挥发性有机物)是反映甲醛浓度的重要指标,因此,也可用于监测CO2浓度。
SGP引脚的定义如下: 典型应用电路如下: 电气特性如下:
二氧化碳浓度含量会影响人类的生活和休息,二氧化碳浓度含量与人体生理反应如下:
350~450ppm:室外环境一般 350~1000ppm:空气清新,呼吸顺畅。 >1000ppm
:感觉空气浑浊,开始昏昏欲睡。
2.生成MDK工程
选择芯片型号
打开STM32CubeMX,打开MCU选择器:
搜索并选择芯片STM32L431RCT6
:
配置时钟源
- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以省略;
我在这里使用外部时钟:
配置串口
板载小熊派开发板ST-Link原理图如下:
我在这里拨打开关AT-MCU
模式,使PC的串口与USART1之间连接。
然后开始配置USART1
:
配置硬件I2C
首先选择将SGP30传感器连接在哪里?I2C如图所示:
然后开始配置I2C接口1:
配置时钟树
STM32L最高主频为80M,所以配置PLL,最后使HCLK = 80Mhz
即可:
生成工程设置
代码生成设置
最后,生成独立的初始化文件:
生成代码
点击GENERATE CODE
即可生成MDK-V5工程:
3. 在MDK编写、编译和下载用户代码
重定向printf函数
参考:【STM32Cube_09】重定向printf从函数到串口输出的方法有很多。
4. 编写SGP30驱动程序
驱动源码:https://github.com/Mculover666/HAL_Driver_Lib
参考Sensirion_Gas_Sensors_SGP30_Datasheet_EN.PDF.pdf进行编程。
宏定义SGP30器件地址
先来编写sgp30.h
头文件,SGP数据手册中已给出30个设备地址:
注意,结合原理图,可定义如下:
#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 fail\r\n");
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 fail\r\n");
return -1;
}
/* 校验接收的测量数据 */
if (CheckCrc8(&recv_buffer[0], 0xFF) != recv_buffer[2]) {
printf("co2 recv data crc check fail\r\n");
return -1;
}
if (CheckCrc8(&recv_buffer[3], 0xFF) != recv_buffer[5]) {
printf("tvoc recv data crc check fail\r\n");
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 <stdio.h>
#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 mculover666\r\n");
if (-1 == sgp30_init()) {
printf("sgp30 init fail\r\n");
/* 因为是裸机,所以直接进入死机 */
while(1);
}
printf("sgp30 init success\r\n");
/* USER CODE END 2 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if( -1 == spg30_read()) {
printf("sgp30 read fail\r\n");
}
else {
printf("sgp30 read success, co2:%4d ppm, tvoc:%4d ppd\r\n", sgp30_data.co2, sgp30_data.tvoc);
}
HAL_Delay(2000);
}
/* USER CODE END 3 */
}
测试结果如图: