【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

相关推荐
崇山峻岭之间14 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious14 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
文祐15 小时前
C语言用双向链表实现单调递减(递增)队列
c语言·开发语言·链表
gugucoding17 小时前
28. 【C语言】通用数据操作:`void *` 与类型无关编程
c语言·开发语言
凉、介17 小时前
ARMv8 架构下的刷 Cache 操作
c语言·笔记·学习·架构·嵌入式·arm
KaifuZeng18 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计19 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu20 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu20 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32
灯厂码农1 天前
C语言动态内存分配完全指南(malloc、calloc、realloc、free)
java·c语言·算法