一、概述
本項(xiàng)目使用 STM32F103C8T6 微控制器作為核心處理器,結(jié)合多個(gè)傳感器和執(zhí)行器,實(shí)現(xiàn)了太陽能熱水器的自動控制。通過對光照、溫度、水位等各種參數(shù)的監(jiān)測和分析,對水泵、電磁閥等設(shè)備進(jìn)行自動控制,從而實(shí)現(xiàn)太陽能熱水器的高效、安全、可靠運(yùn)行。
二、硬件設(shè)計(jì)
(1)模塊組成
太陽能熱水器模塊主要由以下幾個(gè)部分組成:
- 光敏傳感器模塊:用于檢測陽光強(qiáng)度,反映太陽輻射強(qiáng)度和方向。
- 溫度傳感器模塊:用于檢測太陽能集熱器表面和水箱內(nèi)的溫度,并根據(jù)溫度變化調(diào)整水泵、電磁閥等設(shè)備的運(yùn)行狀態(tài)。
- 液位傳感器模塊:用于檢測水箱內(nèi)的液位,并根據(jù)液位高低控制水泵和電磁閥的啟停。
- 水泵模塊:通過控制水泵的啟停,實(shí)現(xiàn)水循環(huán)流動和充水功能。
- 電磁閥模塊:通過控制電磁閥的開關(guān),實(shí)現(xiàn)熱水器的放水和接水功能。
(2)硬件連接
其中,光敏傳感器模塊、溫度傳感器模塊和液位傳感器模塊通過 ADC 接口與 STM32F103C8T6 微控制器進(jìn)行連接;水泵模塊和電磁閥模塊則通過 GPIO 口控制。
連接方式如下:
- 光敏傳感器模塊:將光敏傳感器輸出口與 ADC1 通道10 連接,并用一個(gè)電位器調(diào)整 ADC 的參考電壓,使其范圍在 0-3.3V 之間。
- 溫度傳感器模塊:將 DS18B20 溫度傳感器數(shù)據(jù)線與 GPIOA 的 PA8 引腳連接,并將 VCC 和 GND 分別接到 3.3V 和 GND。
- 液位傳感器模塊:將液位傳感器輸出口與 ADC1 通道11 連接,并用一個(gè)電位器調(diào)整 ADC 的參考電壓。
- 水泵模塊:將水泵正極接到 GPIOB 的 PB1 引腳,將負(fù)極接到電源的負(fù)極。
- 電磁閥模塊:將電磁閥正極接到 GPIOB 的 PB0 引腳,將負(fù)極接到電源的負(fù)極。
三、軟件設(shè)計(jì)
3.1 任務(wù)分配
整個(gè)項(xiàng)目采用 FreeRTOS 系統(tǒng)進(jìn)行開發(fā),實(shí)現(xiàn)數(shù)數(shù)的監(jiān)測和控制,開發(fā)以下幾個(gè)任務(wù):
- 光敏傳感器任務(wù):定時(shí)讀取光敏傳感器輸出口的電壓值,并進(jìn)行數(shù)據(jù)處理,得到當(dāng)前的光照強(qiáng)度。
- 溫度傳感器任務(wù):定時(shí)向 DS18B20 溫度傳感器發(fā)送溫度采樣請求,接收并解析響應(yīng)數(shù)據(jù),得到當(dāng)前的太陽能集熱器表面溫度和水箱內(nèi)溫度。
- 液位傳感器任務(wù):定時(shí)讀取液位傳感器輸出口的電壓值,并進(jìn)行數(shù)據(jù)處理,得到當(dāng)前的水箱水位高度。
- 控制任務(wù):根據(jù)光照強(qiáng)度、溫度和水位高度等參數(shù),決定是否需要啟動水泵或電磁閥等設(shè)備。
偽代碼如下:
void Light_Sensor_Task(void)
{
while (1)
{
voltage = ADC_Get_Voltage(); // 獲取光敏傳感器輸出電壓
light_intensity = voltage * 100 / 3.3f; // 根據(jù)電壓計(jì)算光照強(qiáng)度
vTaskDelay(1000); // 延時(shí) 1s
}
}
?
void Temperature_Sensor_Task(void)
{
while (1)
{
DS18B20_Start_Conversion(); // 向溫度傳感器發(fā)送采樣請求
temperature1 = DS18B20_Read_Temperature(); // 讀取太陽能集熱器表面溫度
temperature2 = DS18B20_Read_Temperature(); // 讀取水箱內(nèi)溫度
vTaskDelay(1000); // 延時(shí) 1s
}
}
?
void Water_Level_Sensor_Task(void)
{
while (1)
{
voltage = ADC_Get_Voltage(); // 獲取液位傳感器輸出電壓
water_level = voltage * 100 / 3.3f; // 根據(jù)電壓計(jì)算水位高度
vTaskDelay(1000); // 延時(shí) 1s
}
}
?
void Control_Task(void)
{
while (1)
{
if (light_intensity > THRESHOLD && temperature1 > THRESHOLD && water_level > THRESHOLD) // 如果各種參數(shù)均符合要求,則啟動水泵和電磁閥
{
GPIO_SetBits(GPIOB, GPIO_Pin_1); // 啟動水泵
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 關(guān)閉電磁閥
}
else // 否則關(guān)閉水泵,打開電磁閥,放水
{
GPIO_ResetBits(GPIOB, GPIO_Pin_1); // 關(guān)閉水泵
GPIO_SetBits(GPIOB, GPIO_Pin_0); // 啟動電磁閥
}
vTaskDelay(1000); // 延時(shí) 1s
}
}
3.2 光敏傳感器任務(wù)
/* 光敏傳感器任務(wù) */
void Light_Sensor_Task(void *pvParameters)
{
uint16_t adc_value;
?
while (1)
{
/* 讀取 ADC 值并計(jì)算光照強(qiáng)度 */
if (HAL_ADC_Start(&hadc1) == HAL_OK)
{
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
adc_value = HAL_ADC_GetValue(&hadc1);
light_intensity = adc_value * 3300 / 4096.0;
}
}
?
vTaskDelay(pdMS_TO_TICKS(1000)); // 延時(shí) 1s
}
}
在函數(shù)中,聲明一個(gè)變量 adc_value
用于存儲讀取到的 ADC 值。使用 if
條件語句檢查 ADC 是否成功啟動,并且使用 HAL_ADC_PollForConversion()
函數(shù)判斷當(dāng)前轉(zhuǎn)換是否完成,如果轉(zhuǎn)換完成,就獲取 ADC 值,并且通過簡單的計(jì)算公式將 ADC 值轉(zhuǎn)換為光照強(qiáng)度值,最后將結(jié)果存儲在 light_intensity
變量中。
3.3 溫度傳感器任務(wù)
/* 溫度傳感器任務(wù) */
void Temperature_Sensor_Task(void *pvParameters)
{
float temperature;
?
/* 初始化 DS18B20 */
DS18B20_Init(&htim2, GPIOA, GPIO_PIN_10);
?
while (1)
{
/* 讀取溫度值 */
temperature = DS18B20_Read_Temperature();
?
/* 將讀取到的溫度值存儲在全局變量中 */
current_temperature = temperature;
?
vTaskDelay(pdMS_TO_TICKS(1000)); // 延時(shí) 1s
}
}
在函數(shù)中,聲明一個(gè)變量 temperature
用于存儲讀取到的溫度值。然后,調(diào)用函數(shù) DS18B20_Init()
初始化 DS18B20 溫度傳感器。使用 DS18B20_Read_Temperature()
函數(shù)讀取溫度值,并且將結(jié)果存儲在 temperature
變量中。最后,將讀取到的溫度值存儲在全局變量 current_temperature
中。
3.4 液位傳感器任務(wù)
/* 液位傳感器任務(wù) */
void Liquid_Level_Sensor_Task(void *pvParameters)
{
uint16_t adc_value;
float voltage;
?
/* 初始化液位傳感器 GPIO 口 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
?
while (1)
{
/* 讀取 ADC 值并計(jì)算電壓值 */
if (HAL_ADC_Start(&hadc1) == HAL_OK)
{
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
adc_value = HAL_ADC_GetValue(&hadc1);
voltage = adc_value * 3.3 / 4096.0;
}
}
?
/* 根據(jù)電壓值計(jì)算液位高度 */
if (voltage < 0.5)
{
liquid_level = 0.0;
}
else if (voltage > 2.5)
{
liquid_level = 100.0;
}
else
{
liquid_level = (voltage - 0.5) * 100.0 / 2.0;
}
?
vTaskDelay(pdMS_TO_TICKS(1000)); // 延時(shí) 1s
}
}
在函數(shù)中,聲明變量 adc_value
和 voltage
,分別用于存儲讀取到的 ADC 值和計(jì)算得到的電壓值。使用 HAL_GPIO_WritePin()
函數(shù)初始化液位傳感器 GPIO 口,將啟用傳感器的引腳設(shè)置為高電平。使用 if
條件語句檢查 ADC 是否成功啟動,并且使用 HAL_ADC_PollForConversion()
函數(shù)判斷當(dāng)前轉(zhuǎn)換是否完成,如果轉(zhuǎn)換完成,就獲取 ADC 值,并且通過簡單的計(jì)算公式將 ADC 值轉(zhuǎn)換為電壓值,并將結(jié)果存儲在 voltage
變量中。
由于需要使用電壓值計(jì)算液位高度,使用 if
條件語句檢查電壓是否小于低液位警戒電壓 0.5V 或者大于高液位警戒電壓 2.5V,如果是則分別將液位高度設(shè)置為 0% 或 100%,否則使用簡單的線性關(guān)系計(jì)算液位高度。
3.5 控制任務(wù)
/* 控制任務(wù) */
void Control_Task(void *pvParameters)
{
float temperature_setpoint = 25.0; // 設(shè)定溫度值
float liquid_level_setpoint = 50.0; // 設(shè)定液位高度值
float temperature_error, liquid_level_error;
float temperature_integral, liquid_level_integral;
float temperature_derivative, liquid_level_derivative;
float temperature_output, liquid_level_output;
?
float kp_temperature = 0.5, ki_temperature = 0.1, kd_temperature = 0.05; // 溫度 PID 參數(shù)
float kp_liquid_level = 0.2, ki_liquid_level = 0.05, kd_liquid_level = 0.02; // 液位高度 PID 參數(shù)
?
while (1)
{
/* 計(jì)算溫度 PID 控制器輸出 */
temperature_error = temperature_setpoint - current_temperature;
temperature_integral += temperature_error;
temperature_derivative = temperature_error - last_temperature_error;
temperature_output = kp_temperature * temperature_error + ki_temperature * temperature_integral + kd_temperature * temperature_derivative;
last_temperature_error = temperature_error;
?
/* 計(jì)算液位高度 PID 控制器輸出 */
liquid_level_error = liquid_level_setpoint - liquid_level;
liquid_level_integral += liquid_level_error;
liquid_level_derivative = liquid_level_error - last_liquid_level_error;
liquid_level_output = kp_liquid_level * liquid_level_error + ki_liquid_level * liquid_level_integral + kd_liquid_level * liquid_level_derivative;
last_liquid_level_error = liquid_level_error;
?
/* 通過 PWM 控制加熱器和水泵電機(jī) */
if (temperature_output > 0.0)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, (uint16_t)(temperature_output * 1000));
}
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0);
}
?
if (liquid_level_output > 0.0)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (uint16_t)(liquid_level_output * 1000));
}
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0);
}
?
vTaskDelay(pdMS_TO_TICKS(10)); // 延時(shí) 10ms
}
}
在函數(shù)中:
(1)定義參數(shù)和變量,包括設(shè)定溫度值、設(shè)定液位高度值、溫度 PID 控制器的參數(shù)、液位高度 PID 控制器的參數(shù)等。使用 while
循環(huán)處理控制邏輯,循環(huán)開始時(shí),計(jì)算溫度 PID 控制器輸出。
(2)計(jì)算當(dāng)前誤差,并將誤差累積到積分項(xiàng)中。計(jì)算誤差變化率,并使用 PID 參數(shù)計(jì)算出輸出值,將結(jié)果存儲在 temperature_output
中,并將當(dāng)前誤差存儲在 last_temperature_error
中以便于下一次計(jì)算,計(jì)算液位高度 PID 控制器輸出。
(3)根據(jù)控制器輸出值通過 PWM 控制加熱器和水泵電機(jī)的運(yùn)行狀態(tài)。如果輸出值大于 0,則啟用電機(jī)或加熱器并設(shè)置對應(yīng)的 PWM 占空比,否則關(guān)閉電機(jī)或加熱器并將 PWM 占空比設(shè)為 0。
審核編輯 黃宇
-
微控制器
+關(guān)注
關(guān)注
48文章
7570瀏覽量
151628 -
太陽能
+關(guān)注
關(guān)注
37文章
3420瀏覽量
114334 -
STM32
+關(guān)注
關(guān)注
2270文章
10910瀏覽量
356591 -
熱水器
+關(guān)注
關(guān)注
5文章
217瀏覽量
27102 -
STM32F103C8T6
+關(guān)注
關(guān)注
108文章
161瀏覽量
83704
發(fā)布評論請先 登錄
相關(guān)推薦
評論