RT-Thread開發之路(5)— 通過ADC採集MQ2煙霧傳感器數據

通過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上訂閱主題,可以接收到:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章