【stm32简单外设篇】- 土壤湿度传感器

一、适用场景

适用场景:室内植物/花圃自动浇水控制、温室环境监测、农业物联网原型、灌溉系统阈值报警、嵌入式入门 ADC 与传感器校准实验。

二、器材清单

土壤湿度传感器模块(常见两类:廉价电阻式探针模块带 AO/DO,或更可靠的电容式传感器,这里用电阻式的) ×1

stm32f103(或其它 STM32)开发板 ×1

若干杜邦线(公对母)×1组

5V/3.3V 稳压电源(按模块规格) ×1

三、工作原理(要点)

电阻式探针:两个金属探针插入土壤,土壤中水份改变电导率,从而改变探针两端电阻(或电压分压)。注意长期会电化学腐蚀,受盐分影响大,稳定性差。

电容式传感器:测量电容随含水量变化(介电常数变化)来推断湿度。优点:抗腐蚀、耐久、稳定;缺点:相对贵一些,需要适配板子/驱动电路。

四、接线示意

VCC → +5V

GND → GND

标准库

PC5 →AO

PA0 →DO

HAL库

PA0 →AO

PA0 →DO

五、示例代码
标准库-DO

cpp 复制代码
void gpio_init()
{
	GPIO_InitTypeDef PA;
	
	//使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

	
	PA.GPIO_Pin = GPIO_Pin_0;
	PA.GPIO_Mode = GPIO_Mode_IPU;
	PA.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&PA);
}


int main()
{
	USART_Config();
	SysTick_Init();
	gpio_init();
	while(1)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == SET)
		{
			Usart_SendString(USART1,"no\n");
		}else
		{
			Usart_SendString(USART1,"water\n");
		}
		Delay_us(1000000);
	}
//	return 0;
}

标准库-AO

cpp 复制代码
int main()
{
	SysTick_Init();
	TIM3_Init();
	USART_Config();
	adctime_Init();
	while(1)
	{
//		sprintf(showlight,"light:%d\n",adc_light);
//		Usart_SendString(USART1,showlight);
//		Delay_us(1000000);
	}
//	return 0;
}
#include "adc_time.h"
#include "stm32f10x.h"

volatile uint16_t adc_light;

void adcgpio_init()
{
	GPIO_InitTypeDef PC;
	
	//使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

	
	PC.GPIO_Pin = GPIO_Pin_5;
	PC.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOC,&PC);
}

void adcmode_init()
{
	ADC_InitTypeDef adc;
	NVIC_InitTypeDef nvic;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2,ENABLE);
	
  adc.ADC_Mode=ADC_Mode_Independent;
	adc.ADC_ScanConvMode = DISABLE;
	adc.ADC_ContinuousConvMode = ENABLE;
	adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	adc.ADC_DataAlign = ADC_DataAlign_Right;
	adc.ADC_NbrOfChannel = 1;
	
	ADC_Init(ADC2,&adc);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC2,ADC_Channel_11,1,ADC_SampleTime_55Cycles5);
	ADC_ITConfig(ADC2,ADC_IT_EOC,ENABLE);

	ADC_Cmd(ADC2,ENABLE);
	
	ADC_ResetCalibration(ADC2);
	while(ADC_GetResetCalibrationStatus(ADC2));
	
	ADC_StartCalibration(ADC2);
	while(ADC_GetCalibrationStatus(ADC2));
	
	ADC_SoftwareStartConvCmd(ADC2,ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
  nvic.NVIC_IRQChannel = ADC1_2_IRQn;
	nvic.NVIC_IRQChannelPreemptionPriority = 3;
	nvic.NVIC_IRQChannelSubPriority = 0;
	nvic.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&nvic);

	
}

void adctime_Init()
{
	adcgpio_init();
	adcmode_init();
}

void ADC1_2_IRQHandler(void)
{
	if(ADC_GetITStatus(ADC2,ADC_IT_EOC) == SET)
	{
		adc_light = ADC_GetConversionValue(ADC2);
	}
	ADC_ClearITPendingBit(ADC2,ADC_IT_EOC);
}
#include "breathing.h"
#include "stm32f10x.h"
#include "stdio.h"
#include "bsp_usart.h"

char showlight[20];
extern volatile uint16_t adc_light;
uint16_t num_final=10;

uint16_t adc_indexlight[] = {
1024,901,792,697,613,
539,474,417,367,323,
284,250,219,193,170,
149,131,116,102,89,
79,69,61,53,47,
41,36,32,28,25,
22,19,17,15,13,
11,10,9,8,7,
6,5,5,4,4,
3,3,2,2,2,
2,1,1,1,1,
0,0,0,0
};

void GPIO_twoInit()
{
	GPIO_InitTypeDef PD;
	
	//使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
	
	PD.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
	PD.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOD,&PD);
	
}

void TIM_GPIO_Init()
{
	GPIO_InitTypeDef PB;
	
	//使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
	
	PB.GPIO_Pin = GPIO_Pin_5;
	PB.GPIO_Speed = GPIO_Speed_50MHz;
	PB.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOB,&PB);
}

void TIM_PWM_Init()
{
	TIM_TimeBaseInitTypeDef TIM;
	TIM_OCInitTypeDef TIM_OC;
	NVIC_InitTypeDef nvic;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	TIM.TIM_Prescaler = (1024-1);  										//配置预装载值
	TIM.TIM_Period = (200-1);													//配置预分频值
	TIM.TIM_ClockDivision = TIM_CKD_DIV1;  						//设置时钟分频系数为不分频
	TIM.TIM_CounterMode = TIM_CounterMode_Up;					//设置向上计数
	TIM_TimeBaseInit(TIM3,&TIM);
	
	TIM_OC.TIM_OCMode = TIM_OCMode_PWM1;							//选择工作模式为PWM
	TIM_OC.TIM_OutputState = TIM_OutputState_Enable;  //使能PWM的输出
	TIM_OC.TIM_Pulse = 0;															//配置脉冲宽度为0
	TIM_OC.TIM_OCPolarity = TIM_OCPolarity_Low;     	//小于脉冲宽度时为低电平
	TIM_OC2Init(TIM3,&TIM_OC);
	
	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);	//使能定时器通道2预装载
	
	TIM_ARRPreloadConfig(TIM3,ENABLE);								//使能重装载寄存器
	
	TIM_Cmd(TIM3,ENABLE);															//使能定时器3
	
  TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
  nvic.NVIC_IRQChannel = TIM3_IRQn;
	nvic.NVIC_IRQChannelPreemptionPriority = 4;
	nvic.NVIC_IRQChannelSubPriority = 0;
	nvic.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&nvic);
	
} 



void TIM3_Init()
{
	TIM_GPIO_Init();
	TIM_PWM_Init();
}	


void TIM3_IRQHandler(void)
{
	uint16_t value;
	static uint16_t num_cnt = 0;
	
	value = (adc_light)/74;
	
	
	if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)
	{
		num_cnt++;
		if(num_cnt > num_final)
		{
			
			TIM_SetCompare2(TIM3,adc_indexlight[value]);
			sprintf(showlight,"light:%d,value:%d\n",adc_light,value);
			Usart_SendString(USART1,showlight);
			num_cnt=0;
		}
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}

HAL库-DO

cpp 复制代码
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == 1)
			HAL_UART_Transmit(&huart1,"土壤湿度正常\r\n", strlen("土壤湿度正常\r\n"), HAL_MAX_DELAY);
		else
			HAL_UART_Transmit(&huart1,"土壤湿度过高\r\n", strlen("土壤湿度过高\r\n"), HAL_MAX_DELAY);
		HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

HAL库-AO

cpp 复制代码
int main(void)
{
 
  /* USER CODE BEGIN 1 */
  char show[20]={0};
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		//#1。启动常规序列
		HAL_ADC_Start(&hadc1);
		//#2.等待转换完成
		HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);
		//#3。获取结果
		uint32_t dr = HAL_ADC_GetValue(&hadc1);
		//#4.把结果转换成电压
		float voltage = dr *(3.3f-0.0f)/4095.0f;
		sprintf(show,"adc:%d,%.2f\n",dr,voltage);
		HAL_UART_Transmit(&huart1,show,strlen(show),HAL_MAX_DELAY);
		HAL_Delay(500);
//		if(voltage>1.5f)//光线暗
//			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_13,GPIO_PIN_SET);// 灭灯
//		else//光线亮
//			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_13,GPIO_PIN_RESET); // 亮灯
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
​

六、讲解视频

https://www.bilibili.com/video/BV19xsmzTE8h/?spm_id_from=333.1387.upload.video_card.click&vd_source=f7dfe1b14f260b9cc3a146d2dbfd0719

https://www.bilibili.com/video/BV19WsmzBE2u/?spm_id_from=333.1387.upload.video_card.click&vd_source=f7dfe1b14f260b9cc3a146d2dbfd0719

https://www.bilibili.com/video/BV1ZjsmzVEDJ/?spm_id_from=333.1387.upload.video_card.click&vd_source=f7dfe1b14f260b9cc3a146d2dbfd0719

相关推荐
小龙报2 小时前
算法通关指南:数据结构和算法篇 --- 队列相关算法题》--- 1. 【模板】队列,2. 机器翻译
c语言·开发语言·数据结构·c++·算法·学习方法·visual studio
晚秋大魔王2 小时前
基于python的jlink单片机自动化批量烧录工具
前端·python·单片机
d111111111d2 小时前
STM32外设学习--TIM定时器--编码器接口
stm32·嵌入式硬件·学习
晨非辰2 小时前
【数据结构初阶】--从排序算法原理分析到代码实现操作,参透插入排序的奥秘!
c语言·开发语言·数据结构·c++·算法·面试·排序算法
2301_795167206 小时前
玩转Rust高级应用 如何避免对空指针做“解引用”操作,在C/C++ 里面就是未定义行为
c语言·c++·rust
某林21210 小时前
ROS2与STM32通信详解
stm32·单片机·嵌入式硬件
ben9518chen11 小时前
嵌入式Linux C语言程序设计九
linux·c语言
下午见。12 小时前
C语言结构体入门:定义、访问与传参全解析
c语言·笔记·学习
EVERSPIN13 小时前
MCU微控制器,N32H47x高性能MCU机器人关节控制方案
单片机·嵌入式硬件·机器人·mcu微控制器