stm32解析dht11传感器1-wire协议
要做个模块stm32带dht11温湿度传感器的。这个传感器使用的是1-wire协议,也就是数据传输只需要一根线就可以了。这个和ds18b20比较相似,但是比18b20要简单一些。因为他的单线数据都是从dht11发送到主机这边来的,主机不需要给它发数据。
百度了一下,在网上大部分例程都是用的io加延时拉模拟他的时序实现的通信。我看到延时这样的函数就很不喜欢,因为我单片机加了还要做其他事情。加了延时就注定要牺牲掉做事儿的时间,导致效率低下。于是乎想到1-wire协议也是高低电平变化,通过判断高电平的时间长短来确定是0还是1,这和红外解码也类似。大部分红外解码用的就是pwm捕获功能来解的,那么这个1-wire协议应该也可以。于是就按这个思路做了下实验,果然可以。
思路呢我是用了两个单片机io口接到dht11的数据线(一个也可以,但是就是程序上麻烦一点,需要变化io口的输入输出状态)。我用了两个io口那就是一个作为输入一个作为输出。这个输出就一个功能,就是给到dht11一个复位信号。这个信号拉低大于18ms再拉高dht11就会温湿度数据输出来,这时候用到的就是单片机的另一个输入口。当然这个输入口一定是要有pwm捕获功能的,我选择的是pa2,是tim15的通道1.
如上图所示就是stm32cubeMx的配置。这里需要重点关注的就是tim15的配置。这里的psc的值是控制计时器的频率的,我这里设置的48-1是因为我的单片机主频设置的是48mhz,这样定时器就是1us记一次数。counter priod这个值则是控制定时器溢出值,也就是计数器记到这个值以后就会重新从0开始记。这个值一定要大于dht11一个数据周期的最大值。比如dht11的0是低50us+高(26-28us),1是低50us+高80us。所以配置的最小值肯定也不能低于130。我们只要把这个数据周期给检测出来,那么我们就知道接收到的是0还是1了。
检测思路是这样的:
我先用单片机的输出口拉低一个20ms的低电平,然后再把输出口输出为高。这时候定时器的计数器清0并打开pwm捕获,这个捕获功能是只能检测上升沿或者下降沿的,我选择检测下降沿。两次下降沿之间的值就是dht11的一个数据周期。判断这个数据周期长短就可以判断出来就收到的是0还是1.看下我串口打印出来的结果:
一共受到41个数据,第一个数据10us是因为是复位信号,所以不算数据,后面的1-41才是真正的dht11数据。可以看到74-76就可以判断为数据0 ,121就是代表数字1了。然后用在中断里面把这些0和1进行解析就可以的到完整的数据。
这是我的中断处理代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim==&htim15) { valueBuf[valueCount] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); valueCount++; if(valueCount>=42) //接收到的编码数量够了41个,关闭定时器 { HAL_TIM_IC_Stop_IT(htim,TIM_CHANNEL_1); } HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET); __HAL_TIM_SET_COUNTER(&htim15,0); } } |
还有我的main函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
int main(void) { /* USER CODE BEGIN 1 */ uint8_t i=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_TIM15_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); __HAL_TIM_SET_COUNTER(&htim15,0); HAL_TIM_IC_Start_IT(&htim15,TIM_CHANNEL_1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ for(i=0;i<valueCount;i++) { printf("-----%d:%d-------\r\n",i,valueBuf[i]); } HAL_Delay(1000); } /* USER CODE END 3 */ } |