一、項目介紹
隨著現代農業的發展,人們對于水資源的合理利用越來越重視。而傳統的灌溉方式往往存在著浪費水資源、勞動力投入大、效率低等問題。因此,設計一款智能灌溉控制系統,可以實現對灌溉水量的精準控制,增加水資源利用率,提高農業生產效率,具有廣泛的應用前景。
當前文章介紹一款高性能的智能灌溉控制系統的開發過程,可自動采集電壓、電流、累計用水量,并根據用戶需要實現自動灌溉、定時灌溉、周期灌溉和手動灌溉等多種模式,同時具備中控室控制、手機短信、現場遙控及現場手動等多種方式控制功能。該系統可以對現場溫濕度限值進行設置和修改,并通過控制器或后臺監控系統完成灌溉起始時間、停止時間、噴灌時間等參數設置。系統顯示功能包括液晶屏以中文菜單方式顯示現場采集數據以及后臺監控系統配大屏幕顯示器,圖形、表格等多種形式動態顯示整個灌溉區運行情況。同時,在電壓、電流或者流量出現異常時,系統可以及時報警。該系統供電為220VAC,流量計量誤差精度為2級,使用二維碼或卡實現預付費功能,通訊使用4G與云平臺連接。
二、設計功能
本系統采用STM32作為主控芯片,并通過AD模塊采集電壓、電流和流量等數據。同時,通過繼電器控制灌溉設備的啟停,使用PWM控制閥門的開合程度,從而實現精確控制灌溉水量。通信模塊則采用4G模塊與云平臺連接,實現遠程監控及控制功能。預付費模塊則使用二維碼或卡實現預付費功能,用戶需在充值后才能使用該系統進行灌溉操作。
系統軟件設計包括采集程序、控制程序、前端程序和后臺程序。其中,采集程序主要負責采集電壓、電流、流量等數據,并將采集到的數據上傳到云平臺;控制程序主要負責控制灌溉設備的啟停和閥門的開合程度,從而實現灌溉控制;前端程序主要負責實現中文菜單方式顯示現場采集數據,并提供灌溉模式選擇、參數設置等功能;后臺程序主要負責實現大屏幕顯示器、圖形、表格等多種形式動態顯示整個灌溉區運行情況。
【1】硬件部分
- MCU:本系統采用STM32作為主控芯片,其具有高性能、低功耗等優點,可滿足該系統的高要求。
- 數據采集模塊:本系統通過AD模塊采集電壓、電流和流量等數據,然后使用MCU進行處理,并將采集到的數據存儲到Flash中。
- 控制模塊:本系統通過繼電器控制灌溉設備的啟停,同時使用PWM控制閥門的開合程度,從而實現精確控制灌溉水量。
- 通信模塊:本系統采用4G模塊與云平臺連接,實現遠程監控及控制功能。
- 預付費模塊:本系統使用二維碼或卡實現預付費功能,用戶需在充值后才能使用該系統進行灌溉操作。
【2】軟件部分
- 采集程序:本系統的采集程序主要負責采集電壓、電流、流量等數據,并將采集到的數據上傳到云平臺。
- 控制程序:本系統的控制程序主要負責控制灌溉設備的啟停和閥門的開合程度,從而實現灌溉控制。
- 前端程序:本系統的前端程序主要負責實現中文菜單方式顯示現場采集數據,并提供灌溉模式選擇、參數設置等功能。
- 后臺程序:本系統的后臺程序主要負責實現大屏幕顯示器、圖形、表格等多種形式動態顯示整個灌溉區運行情況。
三、系統實現
具體實現過程如下:
(1)采集程序
采集程序主要由AD模塊和STM32芯片完成。AD模塊采集電壓、電流和流量等數據,經過濾波和放大處理后,傳輸到STM32芯片上。STM32芯片通過串口將采集到的數據上傳到云平臺,并存儲在Flash中。
(2)控制程序
控制程序主要由繼電器和PWM模塊完成。繼電器用于控制灌溉設備的啟停,PWM模塊則用于控制閥門的開合程度,從而實現精確控制灌溉水量。控制程序通過讀取Flash中存儲的參數,確定灌溉起始時間、停止時間、噴灌時間等操作流程,并根據實時采集到的數據進行動態調整,保證灌溉操作的準確性和穩定性。
(3)前端程序
前端程序主要是通過液晶屏以中文菜單方式顯示現場采集數據,并提供灌溉模式選擇、參數設置等功能。用戶可以通過按鍵或觸摸屏來進行操作,并實時查看灌溉操作的運行情況。此外,用戶還可以通過手機短信、現場遙控或現場手動等方式對灌溉操作進行控制。
(4)后臺程序
后臺程序主要負責實現大屏幕顯示器、圖形、表格等多種形式動態顯示整個灌溉區運行情況,同時還能夠將采集到的數據進行分析和統計,為灌溉管理提供決策參考。
四、核心代碼
【1】電機控制代碼
以下是STM32F103ZET6通過PWM控制直流電機轉速的代碼,并封裝成子函數調用的示例:
首先,需要在STM32CubeMX中配置TIM定時器和GPIO引腳,以及將PWM模式設置為嵌套邊沿對齊模式,然后生成代碼,并在main.c文件中添加以下代碼:
#include "main.h"
#include "stm32f1xx_hal.h"
?
/* TIM handle structure */
TIM_HandleTypeDef htim;
?
/* Function prototypes */
void PWM_Init(TIM_HandleTypeDef *htim, uint32_t channel);
void Set_Motor_Speed(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t speed);
?
int main(void)
{
/* Initialize the HAL Library */
HAL_Init();
?
/* Initialize TIM2 PWM with a frequency of 10 kHz */
PWM_Init(&htim2, TIM_CHANNEL_1);
?
/* Set the motor speed to 50% */
Set_Motor_Speed(&htim2, TIM_CHANNEL_1, 5000);
?
while (1)
{
/* Infinite loop */
}
}
?
/**
* @brief Initializes PWM output on specified TIM channel.
* @param htim: TIM handle structure.
* @param channel: TIM channel to be used for PWM output.
* @retval None
*/
void PWM_Init(TIM_HandleTypeDef *htim, uint32_t channel)
{
TIM_OC_InitTypeDef sConfigOC = {0};
?
/* Configure PWM output on specified TIM channel */
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(htim, &sConfigOC, channel);
?
/* Start PWM output */
HAL_TIM_PWM_Start(htim, channel);
}
?
/**
* @brief Sets the motor speed on specified TIM channel.
* @param htim: TIM handle structure.
* @param channel: TIM channel to be used for PWM output.
* @param speed: Motor speed in units of 1/10,000th of the maximum speed.
* For example, a speed of 5000 would set the motor speed to 50%.
* @retval None
*/
void Set_Motor_Speed(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t speed)
{
uint16_t max_speed = htim->Init.Period;
?
/* Ensure that speed is within range */
if (speed > max_speed)
speed = max_speed;
?
/* Update PWM duty cycle */
__HAL_TIM_SET_COMPARE(htim, channel, speed);
}
在以上代碼中,定義了兩個函數:PWM_Init和Set_Motor_Speed。PWM_Init用于初始化TIM定時器的PWM輸出,并設置指定通道的PWM模式和默認占空比為0。Set_Motor_Speed用于設置電機的轉速,其接收三個參數:TIM句柄結構體,指定的通道,以及電機的轉速(單位為1/10,000最大速度)。該函數會將電機的轉速轉換為PWM占空比,并通過__HAL_TIM_SET_COMPARE函數更新PWM占空比。
最后,可以按照以下步驟將代碼封裝成子函數調用:
- 將以上代碼復制到單獨的.c文件中,并包含必要的頭文件。
- 在該文件中定義一個名為Motor_Control的函數,該函數接收三個參數:TIM句柄結構體,指定的通道,以及電機的轉速。
- 在Motor_Control函數中調用PWM_Init和Set_Motor_Speed函數,并傳遞相應的參數。
- 在main函數中調用Motor_Control函數,傳遞相應的參數。
以下是Motor_Control函數的示例代碼:
#include "motor_control.h"
?
void Motor_Control(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t speed)
{
/* Initialize PWM output on specified TIM channel */
PWM_Init(htim, channel);
?
/* Set the motor speed */
Set_Motor_Speed(htim, channel, speed);
}
在以上示例中,將PWM的初始化和設置電機轉速的函數封裝成了一個名為Motor_Control的函數。可以在需要控制電機轉速的其他地方調用Motor_Control函數即可。
注意,在調用Motor_Control函數之前,需要先定義并初始化TIM句柄結構體,并確保GPIO引腳已經正確配置為TIM模式。此外,如果需要控制多個電機,可以在Motor_Control函數中增加參數以區分不同的電機通道。
以下是motor_control.h頭文件的示例代碼:
cCopy Code#ifndef __MOTOR_CONTROL_H__
#define __MOTOR_CONTROL_H__
?
#include "stm32f1xx_hal.h"
?
/* Function prototypes */
void PWM_Init(TIM_HandleTypeDef *htim, uint32_t channel);
void Set_Motor_Speed(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t speed);
void Motor_Control(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t speed);
?
#endif /* __MOTOR_CONTROL_H__ */
在以上頭文件中,聲明了三個函數:PWM_Init,Set_Motor_Speed和Motor_Control,并包含必要的頭文件。
【2】電壓、電流采集
為了采集220V抽水電機的用電量和當前電壓,當前使用STM32F103ZET6的ADC(模數轉換器)來測量電壓和電流,并通過乘法器計算電功率和電能。
下面是實現方案和實現代碼:
- 選擇合適的傳感器: 為了測量電壓,可以使用AC-AC變壓器將220V交流電壓降至低電平,再使用電阻分壓器將電壓信號調整在ADC的輸入范圍內。 為了測量電流,可以使用霍爾傳感器或者電阻式傳感器,將電流信號轉換成電壓信號,然后通過電阻分壓器調整信號范圍。
- 配置ADC: 使用STM32CubeMX軟件選擇相應的引腳和配置ADC模塊,設置采樣頻率、參考電壓等參數。需要注意的是,ADC模塊只能同時轉換一路模擬信號,因此需要輪流采樣電壓和電流信號。
- 計算電流、電壓、功率和能量: 將電壓和電流信號轉換成數字值后,可以使用下面的公式計算電流、電壓、功率和能量:
Copy Code電流 = AD值 / 靈敏度
電壓 = AD值 / 分壓比
功率 = 電壓 * 電流
能量 = 功率 * 時間
其中,靈敏度是傳感器的轉換系數,分壓比是電阻分壓器的比值,時間可以通過定時器計算。
- 輸出數據: 將測量的電流、電壓、功率和能量輸出到串口或者LCD顯示屏上。可以設置一個定時器,在一定時間間隔內輸出一次數據。
實現代碼:
#include "stm32f1xx_hal.h"
?
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim2;
?
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_TIM2_Init(void);
?
uint16_t ad_val_ch1, ad_val_ch2;
float voltage, current, power, energy;
?
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM2_Init();
?
while (1)
{
// ADC采樣電壓信號
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
ad_val_ch1 = HAL_ADC_GetValue(&hadc1);
voltage = ad_val_ch1 * 3.3 / 4096 * 10; // 假設分壓比為10
?
// ADC采樣電流信號
HAL_TIM_Base_Start(&htim2);
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
ad_val_ch2 = HAL_ADC_GetValue(&hadc1);
current = ad_val_ch2 * 3.3 / 4096 * 50; // 假設靈敏度為50mV/A
?
// 計算功率和能量
power = voltage * current;
energy += power * 0.1; // 假設定時器時間間隔為100ms
?
// 輸出測量結果
printf("Voltage: %.2f Vrn", voltage);
printf("Current: %.2f Arn", current);
printf("Power: %.2f Wrn", power);
printf("Energy: %.2f Jrn", energy);
?
HAL_Delay(1000); // 假設數據輸出間隔為1s
}
}
?
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
?
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
?
RCC_OscInitStruct.OscillatorType = RCCRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
?
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
?
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
?
__HAL_RCC_ADC1_CLK_ENABLE();
?
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
?
sConfig.Channel = ADC_CHANNEL_0; // 假設測量電壓的ADC通道為0
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
?
sConfig.Channel = ADC_CHANNEL_1; // 假設測量電流的ADC通道為1
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
?
static void MX_TIM2_Init(void)
{
__HAL_RCC_TIM2_CLK_ENABLE();
?
htim2.Instance = TIM2;
htim2.Init.Prescaler = 7200 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 10000 - 1; // 假設定時器時間間隔為100ms
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
}
?
void Error_Handler(void)
{
while (1)
{
}
}
?
#ifdef USE_FULL_ASSERT
?
void assert_failed(char *file, uint32_t line)
{
}
?
#endif
-
單片機
+關注
關注
6040文章
44594瀏覽量
636941 -
控制系統
+關注
關注
41文章
6642瀏覽量
110725 -
4G
+關注
關注
15文章
5528瀏覽量
119294 -
STM32
+關注
關注
2270文章
10915瀏覽量
356774
發布評論請先 登錄
相關推薦
評論