通過ADC採集MQ2煙霧傳感器數據
一、硬件準備
小熊派+E53_SF1擴展模塊,如下所示:
二、開啓ADC
打開board.h
文件,找到ADC的配置處,按照其提示使用:
首先,打開【RT-Thread Settings】,找到ADC設備驅動程序,將其選中,然後保存使之生效
通過查看原理圖,可以知道,其連接到的引腳是ADC1的通道3,使用將board.h
的ADC1的註釋打開
接下來使用CubeMx生成HAL_ADC_MspInit()函數,將ADC配置爲如圖所示:
將HAL_ADC_MspInit()
複製到board.c
裏
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**ADC1 GPIO Configuration
PC2 ------> ADC1_IN3
*/
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
然後打開stm32xxxx_hal_config.h
文件,將#define HAL_ADC_MODULE_ENABLED
的註釋去掉:
然後要將board.c
中的SystemClock_Config()
函數也替換掉,因爲要加入ADC外設的時鐘初始化,
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE;
PeriphClkInit.PLLSAI1.PLLSAI1M = 1;
PeriphClkInit.PLLSAI1.PLLSAI1N = 8;
PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
}
三、讀取ADC的值
編寫main.c爲如下所示:
#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>
/* 獲取LED引腳對應的編號 */
#define LED0_PIN GET_PIN(C, 13)
#define ADC_DEV_NAME "adc1" /* ADC 設備名稱 */
#define ADC_DEV_CHANNEL 3 /* ADC 通道 */
#define REFER_VOLTAGE 330 /* 參考電壓 3.3V,數據精度乘以100保留2位小數*/
#define CONVERT_BITS (1 << 12) /* 轉換位數爲12位 */
rt_adc_device_t adc_dev; /* ADC 設備句柄 */
rt_uint32_t value;
int main(void)
{
int count = 1, vol;
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
/* 查找設備 */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
/* 使能設備 */
rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
while (count++)
{
/* 讀取採樣值 */
value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
/* 轉換爲對應電壓值 */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* set LED0 pin level to high or low */
rt_pin_write(LED0_PIN, count % 2);
rt_thread_mdelay(1000);
}
return RT_EOK;
}
編譯,燒錄啓動,使用杜邦線將該引腳分別接到GND和3V3,可以看到,讀取成功
然後我們接上E53_SF1擴展模塊,同時修改主函數,將電壓值轉換爲煙霧濃度:
/* 電壓轉換成煙霧濃度 */
RS = (3.3f - Vrl) / Vrl * RL;
ppm = 613.9f * pow(RS/R0, -2.074f);
sprintf(data_buf,"the voltage is :%f V, ppm is : %f \n", Vrl, ppm);
rt_kprintf(data_buf);
公式參考自https://blog.csdn.net/qq_41422043/article/details/89138213
運行結果如下
四、發送數據到雲端
結合之前的內容:RT-Thread開發之路(4)— MQTT通信
我們將煙霧濃度數據通過郵箱發送到MQTT通信線程,然後發佈主題消息到EMQ,我們先在app_mqtt.c
裏定義一個郵箱結構體,
/* 定義一個煙霧濃度郵箱控制塊結構體指針*/
rt_mailbox_t mq2_mailbox = RT_NULL;
然後在app_mqtt_init()創建,注意,要在創建線程之前創建郵箱
static int app_mqtt_init(void)
{
rt_err_t rt_err;
/* 使用動態創建方法創建一個郵箱 */
mq2_mailbox = rt_mb_create ("mq2 mailbox", 4, RT_IPC_FLAG_FIFO); /* 採用FIFO方式進行線程等待 */
/* 判斷郵箱是否創建成功 */
if( mq2_mailbox != RT_NULL)
rt_kprintf("key mailbox create succeed. \n");
else
rt_kprintf("key mailbox create failure. \n");
/* 創建MQTT線程*/
app_mqtt_thread = rt_thread_create("app_mqtt thread",
app_mqtt_thread_entry, RT_NULL, 2048, 6, 10);
/* 如果獲得線程控制塊,啓動這個線程 */
if (app_mqtt_thread != RT_NULL)
rt_err = rt_thread_startup(app_mqtt_thread);
else
rt_kprintf("app_mqtt_thread create failure !!! \n");
/* 判斷線程是否啓動成功 */
if( rt_err == RT_EOK)
rt_kprintf("app_mqtt_thread startup ok. \n");
else
rt_kprintf("app_mqtt_thread startup err. \n");
return rt_err;
}
然後獲取到煙霧數據後發送到郵箱:
sprintf(msg_buf,"{\"ppm\":%.2f }", ppm);
rt_mb_send(mq2_mailbox, (rt_uint32_t)msg_buf);
在MQTT線程中將其發佈到主題:
static void app_mqtt_thread_entry(void *parameter)
{
......
while(1)
{
/* 從郵箱中收取郵件 */
if (rt_mb_recv(mq2_mailbox, (rt_ubase_t *)&msg_buf, RT_WAITING_FOREVER) == RT_EOK)
{
paho_mqtt_publish(&client, QOS1, "BearPi_Pub", (char *)msg_buf);
}
rt_thread_mdelay(100);
}
}
運行後,在EMQ上訂閱主題,可以接收到: