项目需求1.支持模拟多线程功能2.支持门禁功能3.支持燃气报警功能4.支持环境数据上传OneNET功能5.支持蓝牙、语音控制客厅/卧室灯硬件清单矩阵键盘OLED蜂鸣器W25Q128继电器烟雾传感器DHT11光敏电阻传感器SU-03T蓝牙模块杜邦线上官二号ST-LinkUSB转TTL硬件接线做项目的时候一定要进行引脚的分配。首先就是可以把固定的引脚先给定下来。比如说串口A9、A10串口1 A2、A3串口2 B10、B11串口3。还有就是SPIA4、A5、A6、A7。ADC呢用的是ADC1-----A0、A1.其余的就都是普通的GPIO口当然我们的OLED屏幕 用的是IIC的协议但我们一般都是用GPIO口软件来模拟IIC的协议这样引脚会很灵活如果说用的是硬件的IIC的话就只能固定接到某处。其余的gpio口就按方便随意的去接了。项目框图代码示例其中beep、keyboard、led、lock、oled、password、w25q128是智能门禁项目中的。多线程实验中的tasks 、和中断点灯实验中的timer、ADC多通道实验中的adc。下划线加驱动wifi控制风扇中的fan本身就是继电器。蓝牙控制插座实验的bluetooth。呼吸灯实验中的pwm。voice是自建的。dht11、onenet、esp8266在温湿度上传onenet实验中。“品”魔法棒哦可以发现我们这个智能家居的项目呢是之前好几个项目的融合。我们可以发现右侧的框图呢就是我们之前的智能门禁系统的流程图定时器呢用来模拟多线程。所以在智能门禁系统的代码基础上进行改动。我们首先打开工程。先对门禁系统进行改动首先就是矩阵键盘的引脚调整。像w25q128密码存储就没法改引脚OLED也没改变。 主要改动就是引脚和对应的中断线。矩阵键盘的引脚修改#include keyboard.h #include delay.h static uint8_t key_value 0; void keyboard_init(void) { GPIO_InitTypeDef gpio_initstruct; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_12; gpio_initstruct.Mode GPIO_MODE_IT_FALLING; gpio_initstruct.Pull GPIO_PULLUP; gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, gpio_initstruct); gpio_initstruct.Pin GPIO_PIN_13 | GPIO_PIN_14; gpio_initstruct.Mode GPIO_MODE_INPUT; gpio_initstruct.Pull GPIO_PULLDOWN; HAL_GPIO_Init(GPIOB, gpio_initstruct); gpio_initstruct.Pin GPIO_PIN_8; HAL_GPIO_Init(GPIOA, gpio_initstruct); HAL_NVIC_SetPriority(EXTI0_IRQn, 3, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_NVIC_SetPriority(EXTI1_IRQn, 3, 0); HAL_NVIC_EnableIRQ(EXTI1_IRQn); HAL_NVIC_SetPriority(EXTI2_IRQn, 3, 0); HAL_NVIC_EnableIRQ(EXTI2_IRQn); HAL_NVIC_SetPriority(EXTI15_10_IRQn, 3, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); } void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } void EXTI1_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1); } void EXTI2_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); } void EXTI15_10_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { uint8_t row 0, column 0; if(key_value ! 0) return; //确认行 if(GPIO_Pin GPIO_PIN_0) row 0x10; else if(GPIO_Pin GPIO_PIN_1) row 0x20; else if(GPIO_Pin GPIO_PIN_2) row 0x30; else if(GPIO_Pin GPIO_PIN_12) row 0x40; //确定列 if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) GPIO_PIN_SET) { delay_ms(10); while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13)) column 0x01; } else if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) GPIO_PIN_SET) { delay_ms(10); while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14)) column 0x02; } else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) GPIO_PIN_SET) { delay_ms(10); while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)) column 0x03; } if(row ! 0 column ! 0) key_value row | column; } uint8_t keyboard_get_value(void) { uint8_t ch 0; if(key_value ! 0) { if(key_value 0x11) ch 1; else if(key_value 0x12) ch 2; else if(key_value 0x13) ch 3; else if(key_value 0x21) ch 4; else if(key_value 0x22) ch 5; else if(key_value 0x23) ch 6; else if(key_value 0x31) ch 7; else if(key_value 0x32) ch 8; else if(key_value 0x33) ch 9; else if(key_value 0x41) ch *; else if(key_value 0x42) ch 0; else if(key_value 0x43) ch #; delay_ms(400); key_value 0x00; } return ch; }模拟多线程我们之前模拟多线程的时候用的是systick这里也可以用实际上也可以用定时器timer去模拟多线程。 因此就要修改原有的delay。进入到这里把原有的delay改名字就可以这里是delay1。然后将原本用systick模拟多线程的delay复制过来。然后将多线程实验BSP中的tasks 和中断点灯实验中的timer拿过来到本项目的BSP中。新加进来的-----“品”魔法棒还有定时器的驱动文件。tasks修改一共3个任务。改完timer_isr名字后.h文件中也要对应的声明。再将其放入到timer.c中的回调函数中。#include tasks.h #include stdio.h #include adc.h #include beep.h #include fan.h #include led.h #include bluetooth.h #include voice.h #include dht11.h #include string.h #include onenet.h uint32_t task1_cnt 0; uint32_t task2_cnt 0; uint32_t task3_cnt 0; uint8_t task1_flag 0; uint8_t task2_flag 0; uint8_t task3_flag 0; uint16_t adc_value[2] {0}; //adc result get uint8_t led_status 0, voice_status_new 0, voice_status_old 0; void timer_isr(void) //把systick改成timer { if (task1_cnt 1) // 每500ms检测一次语音定时器计一个数500ms。 task1_cnt; else { task1_flag 1; task1_cnt 0; } if (task2_cnt 2) // 一秒检测一次 燃气 task2_cnt; else { task2_flag 1; task2_cnt 0; } if (task3_cnt 20) // 每十秒检测一次 task3_cnt; else { task3_flag 1; task3_cnt 0; } task_light_control(); task_gas_alarm(); task_env_upload(); adc_result_get(adc_value); // printf(value1:%.2f, value2:%.2f\r\n, (float)adc_value[0]/4096*3.3, (float)adc_value[1]/4096*3.3); } void task_light_control(void) { if(task1_flag 0) return; task1_flag 0; led_status bt_value_get(); voice_status_new voice_value_get(); if(voice_status_old ! voice_status_new)//有改动的时候 语音才会去改变灯的状态位 { //否则将会使蓝牙控制部分失效 led_status voice_status_new; voice_status_old voice_status_new; //更新 新状态 } switch(led_status) { case 0: led_living_off(); break; case 1: led_living_low(); break; case 2: led_living_medium(); break; case 3: led_living_high(); break; case 4: led_bedroom_on(); break; case 5: led_bedroom_off(); break; default: break; } } void task_gas_alarm(void) //我们的任务二燃气报警用到了风扇、蜂鸣器、MQ-2用的ADC { if(task2_flag 0) return; task2_flag 0; float gas_value (float)adc_value[0]/4096*3.3; printf(燃气值%.2f\r\n, gas_value); if(gas_value 1.0) { beep_on(); fan_on(); } else { beep_off(); fan_off(); } } void task_env_upload(void) { if(task3_flag 0) return; task3_flag 0; uint8_t dht11_data[4]; uint8_t data_send_buf[512]; dht11_read(dht11_data); printf(湿度%d.%dRH ,, dht11_data[0], dht11_data[1]); printf(温度%d.%d℃\r\n, dht11_data[2], dht11_data[3]); float light_value (float)adc_value[1]/4096*3.3; memset(data_send_buf, 0, sizeof(data_send_buf)); //传之前先清空 sprintf((char *)data_send_buf, {\id\:\1720777789227\,\version\:\1.0\,\params\:{\CurrentTemperature\:{\value\:%d.%d},\RelativeHumidity\:{\value\:%d.%d},\LightLuxValue\:{\value\:%.1f}}} , dht11_data[2], dht11_data[3], dht11_data[0], dht11_data[1], light_value); mqtt_publish_data(POST_TOPIC, (char *)data_send_buf, 0); }timer.c#include timer.h #include tasks.h TIM_HandleTypeDef timer_handle {0}; //定时器初始化函数 void timer_init(uint16_t arr, uint16_t psc) { timer_handle.Instance TIM2; timer_handle.Init.Prescaler psc; timer_handle.Init.Period arr; timer_handle.Init.CounterMode TIM_COUNTERMODE_UP; timer_handle.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_Base_Init(timer_handle); HAL_TIM_Base_Start_IT(timer_handle); } //msp函数 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) { if(htim-Instance TIM2) { __HAL_RCC_TIM2_CLK_ENABLE(); HAL_NVIC_SetPriority(TIM2_IRQn, 2, 2); HAL_NVIC_EnableIRQ(TIM2_IRQn); } } //中断服务函数 void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(timer_handle); } //更新中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM2) { timer_isr(); //每500ms产生一次中断记一个数 } }fan继电器只需将对应的引脚改成A12即可。ADCADC主要测量的是燃气MQ-2这个模块另一个就是光敏电阻传感器。共两个通道。PA0---ADC1_IN0、PA1---ADC1_IN1。#include adc.h #include string.h ADC_HandleTypeDef adc_handle {0}; DMA_HandleTypeDef dma_handle {0}; uint16_t adc_result[2] {0}; //承接 两条ADC通道 转换结果的全局变量 void adc_config(void) { adc_handle.Instance ADC1; adc_handle.Init.DataAlign ADC_DATAALIGN_RIGHT; adc_handle.Init.ScanConvMode ADC_SCAN_ENABLE; adc_handle.Init.ContinuousConvMode ENABLE; adc_handle.Init.NbrOfConversion 2; //转换的通道个数 2个 adc_handle.Init.DiscontinuousConvMode DISABLE; adc_handle.Init.NbrOfDiscConversion 0; adc_handle.Init.ExternalTrigConv ADC_SOFTWARE_START; HAL_ADC_Init(adc_handle); HAL_ADCEx_Calibration_Start(adc_handle); } void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) { if(hadc-Instance ADC1) { RCC_PeriphCLKInitTypeDef adc_clk_init {0}; GPIO_InitTypeDef gpio_init_struct {0}; __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); gpio_init_struct.Pin GPIO_PIN_0 | GPIO_PIN_1; //通道对应的引脚 gpio_init_struct.Mode GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, gpio_init_struct); adc_clk_init.PeriphClockSelection RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(adc_clk_init); } } void dma_config(void) { __HAL_RCC_DMA1_CLK_ENABLE(); dma_handle.Instance DMA1_Channel1; dma_handle.Init.Direction DMA_PERIPH_TO_MEMORY; //内存相关配置 dma_handle.Init.MemDataAlignment DMA_MDATAALIGN_HALFWORD; dma_handle.Init.MemInc DMA_MINC_ENABLE; //外设相关配置 dma_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_HALFWORD; dma_handle.Init.PeriphInc DMA_PINC_DISABLE; dma_handle.Init.Priority DMA_PRIORITY_MEDIUM; dma_handle.Init.Mode DMA_CIRCULAR; HAL_DMA_Init(dma_handle); __HAL_LINKDMA(adc_handle, DMA_Handle, dma_handle); } void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime) { ADC_ChannelConfTypeDef adc_ch_config {0}; adc_ch_config.Channel ch; adc_ch_config.Rank rank; adc_ch_config.SamplingTime stime; HAL_ADC_ConfigChannel(hadc, adc_ch_config); } void adc_dma_init(void) //以前这个函数是有传入参数的让转换的结果直接传入这变量中 { adc_config(); adc_channel_config(adc_handle, ADC_CHANNEL_0, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); adc_channel_config(adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_2, ADC_SAMPLETIME_239CYCLES_5); dma_config(); HAL_ADC_Start_DMA(adc_handle, (uint32_t *)adc_result, 2); //2个通道 } void adc_result_get(uint16_t *result) //新定义的 { memcpy(result, adc_result, sizeof(adc_result)); //拷贝到这变量中 string.h }bluetooth在蓝牙这里部分我们需要增加一下串口部分的内容。也就是之前总提到过的串口2的msp函数要放在uart1的msp函数当中。实际上处理串口接收到的 第0位 即可。如果蓝牙发送的是字符的1后续要处理成数字的1直接减去字符的0就可以了。#include sys.h #include bluetooth.h #include string.h #include stdarg.h UART_HandleTypeDef uart2_handle; /* uart2句柄 */ uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE]; /* uart2接收缓冲区 */ uint16_t uart2_rx_len 0; /* uart2接收字符长度 */ uint8_t bt_rx 0; //保存接收的值 /** * brief 串口1初始化函数 * param baudrate: 波特率, 根据自己需要设置波特率值 * retval 无 */ void bt_init(uint32_t baudrate) { /*uart2 初始化设置*/ uart2_handle.Instance USART2; /* USART1 */ uart2_handle.Init.BaudRate baudrate; /* 波特率 */ uart2_handle.Init.WordLength UART_WORDLENGTH_8B; /* 字长为8位数据格式 */ uart2_handle.Init.StopBits UART_STOPBITS_1; /* 一个停止位 */ uart2_handle.Init.Parity UART_PARITY_NONE; /* 无奇偶校验位 */ uart2_handle.Init.HwFlowCtl UART_HWCONTROL_NONE; /* 无硬件流控 */ uart2_handle.Init.Mode UART_MODE_TX_RX; /* 收发模式 */ HAL_UART_Init(uart2_handle); /* HAL_UART_Init()会使能uart2 */ } /** * brief uart2接收缓冲区清除 * param 无 * retval 无 */ void uart2_rx_clear(void) { memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf)); /* 清空接收缓冲区 */ uart2_rx_len 0; /* 接收计数器清零 */ } /** * brief 串口2中断服务函数 * note 在此使用接收中断及空闲中断实现不定长数据收发 * param 无 * retval 无 */ void USART2_IRQHandler(void) { uint8_t receive_data 0; if(__HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_RXNE) ! RESET){ /* 获取接收RXNE标志位是否被置位 */ if(uart2_rx_len sizeof(uart2_rx_buf)) /* 如果接收的字符数大于接收缓冲区大小 */ uart2_rx_len 0; /* 则将接收计数器清零 */ HAL_UART_Receive(uart2_handle, receive_data, 1, 1000); /* 接收一个字符 */ uart2_rx_buf[uart2_rx_len] receive_data; /* 将接收到的字符保存在接收缓冲区 */ } if (__HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_IDLE) ! RESET) /* 获取接收空闲中断标志位是否被置位 */ { printf(bt recv: %s\r\n, uart2_rx_buf); /* 将接收到的数据打印出来 */ bt_rx uart2_rx_buf[0]; uart2_rx_clear(); __HAL_UART_CLEAR_IDLEFLAG(uart2_handle); /* 清除UART总线空闲中断 */ } } //void bt_send(char *send_buf, uint8_t size) //{ // HAL_UART_Transmit(uart2_handle, (uint8_t *)send_buf, size, 100); //} void bt_send(char * format, ...) { uint8_t send_buf[128] {0}; va_list arg; va_start(arg, format); vsprintf((char *)send_buf, format, arg); va_end(arg); HAL_UART_Transmit(uart2_handle, send_buf, sizeof(send_buf), 100); } uint8_t bt_value_get(void) { uint8_t temp bt_rx - 0; bt_rx 0; return temp; }led#include led.h #include sys.h TIM_HandleTypeDef pwm_handle {0}; // init函数 void led_living_init(uint16_t arr, uint16_t psc) //客厅灯 PB6 定时器4通道1 { TIM_OC_InitTypeDef pwm_config {0}; pwm_handle.Instance TIM4; pwm_handle.Init.Prescaler psc; pwm_handle.Init.Period arr; pwm_handle.Init.CounterMode TIM_COUNTERMODE_UP; HAL_TIM_PWM_Init(pwm_handle); pwm_config.OCMode TIM_OCMODE_PWM1; pwm_config.Pulse 0; pwm_config.OCPolarity TIM_OCPOLARITY_HIGH; //高电平触发 HAL_TIM_PWM_ConfigChannel(pwm_handle, pwm_config, TIM_CHANNEL_1); HAL_TIM_PWM_Start(pwm_handle, TIM_CHANNEL_1); } //msp函数 void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { if(htim-Instance TIM4) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 __HAL_RCC_TIM4_CLK_ENABLE(); //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_6; // 两个LED对应的引脚 gpio_initstruct.Mode GPIO_MODE_AF_PP; // 推挽输出 gpio_initstruct.Pull GPIO_PULLUP; // 上拉 gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(GPIOB, gpio_initstruct); } } //修改CCR值的函数 void pwm_compare_set(uint16_t val) { __HAL_TIM_SET_COMPARE(pwm_handle, TIM_CHANNEL_1, val); } void led_living_off(void) { pwm_compare_set(0); } void led_living_low(void) { pwm_compare_set(150); } void led_living_medium(void) { pwm_compare_set(300); } void led_living_high(void) { pwm_compare_set(450); } //初始化GPIO函数 void led_bedroom_init(void) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟 //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_11; // 两个LED对应的引脚 gpio_initstruct.Mode GPIO_MODE_OUTPUT_PP; // 推挽输出 gpio_initstruct.Pull GPIO_PULLUP; // 上拉 gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(GPIOA, gpio_initstruct); //关闭LED led_bedroom_off(); } //点亮LED1的函数 void led_bedroom_on(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET); // 拉低LED1引脚点亮LED1 } //熄灭LED1的函数 void led_bedroom_off(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET); // 拉高LED1引脚熄灭LED1 } void led_init(void) { led_bedroom_init(); led_living_init(500 - 1, 72 - 1); }voice语音识别模块 我们连接的是PB4PB5。像我们这里为什么只用两根线呢 这是因为板子上空余的只有这两个gpio口了所以要根据实际场景进行芯片选型。我们这里呢可以用二进制的方式去控制客厅灯的四种状态。光代码写完可不行还需要去烧录语音控制模块用到之前说过的智能公元。进行四种状态的引脚A25 A26设置。智能公元/AI产品零代码平台#include voice.h void voice_init(void) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_5 | GPIO_PIN_4; // 两个LED对应的引脚 gpio_initstruct.Mode GPIO_MODE_INPUT; // 推挽输出 gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(GPIOB, gpio_initstruct); } uint8_t voice_value_get(void) { return ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) 1) | HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)); //pin4读取之后左移一位再读取pin5. }esp8266主要就是改他的串口我们这里用的是串口3。esp8266_handle.Instance USART3;对应的msp函数也要加一下。对应的引脚要看好这里是PB10 PB11。这里吧串口三的空闲中断给去掉了因为串口三是不使用空闲中断的。还有就是esp8266.h中的wifi账号密码。void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef gpio_init_struct; if (huart-Instance USART1) /* 如果是串口1进行串口1 MSP初始化 */ { __HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能串口TX脚时钟 */ __HAL_RCC_USART1_CLK_ENABLE(); /* 使能串口时钟 */ gpio_init_struct.Pin GPIO_PIN_9; /* 串口发送引脚号 */ gpio_init_struct.Mode GPIO_MODE_AF_PP; /* 复用推挽输出 */ gpio_init_struct.Pull GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */ HAL_GPIO_Init(GPIOA, gpio_init_struct); gpio_init_struct.Pin GPIO_PIN_10; /* 串口RX脚 模式设置 */ gpio_init_struct.Mode GPIO_MODE_AF_INPUT; HAL_GPIO_Init(GPIOA, gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */ HAL_NVIC_EnableIRQ(USART1_IRQn); /* 使能USART1中断通道 */ HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); /* 最低优先级:抢占优先级3子优先级3 */ __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); /* 使能UART1接收中断 */ __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); /* 使能UART1总线空闲中断 */ } else if (huart-Instance USART2) /* 如果是串口2进行串口2 MSP初始化 */ { __HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能串口TX脚时钟 */ __HAL_RCC_USART2_CLK_ENABLE(); /* 使能串口时钟 */ gpio_init_struct.Pin GPIO_PIN_2; /* 串口发送引脚号 */ gpio_init_struct.Mode GPIO_MODE_AF_PP; /* 复用推挽输出 */ gpio_init_struct.Pull GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */ HAL_GPIO_Init(GPIOA, gpio_init_struct); gpio_init_struct.Pin GPIO_PIN_3; /* 串口RX脚 模式设置 */ gpio_init_struct.Mode GPIO_MODE_AF_INPUT; HAL_GPIO_Init(GPIOA, gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */ HAL_NVIC_EnableIRQ(USART2_IRQn); /* 使能USART2中断通道 */ HAL_NVIC_SetPriority(USART2_IRQn, 3, 3); /* 最低优先级:抢占优先级3子优先级3 */ __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); /* 使能UART2接收中断 */ __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); /* 使能UART2总线空闲中断 */ } else if (huart-Instance USART3) { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_USART3_CLK_ENABLE(); gpio_init_struct.Pin GPIO_PIN_10; gpio_init_struct.Mode GPIO_MODE_AF_PP; gpio_init_struct.Pull GPIO_PULLUP; gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, gpio_init_struct); gpio_init_struct.Pin GPIO_PIN_11; gpio_init_struct.Mode GPIO_MODE_AF_INPUT; HAL_GPIO_Init(GPIOB, gpio_init_struct); HAL_NVIC_EnableIRQ(USART3_IRQn); HAL_NVIC_SetPriority(USART3_IRQn, 3, 3); __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); // __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); } }dht11只需要改一下引脚号就可以了这里是PB3.在h文件中改宏。要注意这里PB3的主功能复位后是JTDO不是一个普通的GPIO口是用来烧录的。如果想让他变成一个普通GPIO口的话呢需要两行代码。使能AFIO时钟禁用JTAG接口。#include dht11.h #include delay.h #include string.h #include stdio.h char dht11_data[5] {0}; void dht11_gpio_input(void) { GPIO_InitTypeDef gpio_initstruct; DHT11_CLK_ENABLE(); gpio_initstruct.Pin DHT11_PIN; gpio_initstruct.Mode GPIO_MODE_INPUT; gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, gpio_initstruct); } void dht11_gpio_output(void) { GPIO_InitTypeDef gpio_initstruct; DHT11_CLK_ENABLE(); gpio_initstruct.Pin DHT11_PIN; gpio_initstruct.Mode GPIO_MODE_OUTPUT_PP; gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, gpio_initstruct); } void dht11_start(void) { dht11_gpio_output(); DHT11_DQ_OUT(1); DHT11_DQ_OUT(0); delay_ms(20); DHT11_DQ_OUT(1); dht11_gpio_input(); while(DHT11_DQ_IN); //等待DHT11拉低电平 while(!DHT11_DQ_IN); //等待DHT11拉高电平 while(DHT11_DQ_IN); //等待DHT11拉低电平 } uint8_t dht11_read_byte(void) { uint8_t temp 0; uint8_t i 0; uint8_t read_data 0; for(i 0; i 8; i) { while(!DHT11_DQ_IN); delay_us(50); if(DHT11_DQ_IN 1) { temp 1; while(DHT11_DQ_IN); } else temp 0; read_data read_data 1; read_data | temp; } return read_data; } void dht11_read(uint8_t *result) { uint8_t i 0; __HAL_RCC_AFIO_CLK_ENABLE(); //使能AFIO时钟 __HAL_AFIO_REMAP_SWJ_NOJTAG(); //禁用JTAG接口 dht11_start(); dht11_gpio_input(); for(i 0; i 5; i) dht11_data[i] dht11_read_byte(); if(dht11_data[0] dht11_data[1] dht11_data[2] dht11_data[3] dht11_data[4]) { memcpy(result, dht11_data, 4); // printf(湿度%d.%dRH ,, dht11_data[0], dht11_data[1]); // printf(温度%d.%d℃\r\n, dht11_data[2], dht11_data[3]); } // delay_ms(2000); }onenet现在呢我们就可以把这几个的值通过esp8266上传到onenet的平台上。首先呢就是要先创建好产品和设备。建好之后可以点击产品开发然后看产品中有各种的属性。要记住所能用到的标识符json字符串中会用到。湿度 光照度 温度。设备名称 产品id 和密钥也都要保存下来。不用看下图这里的内容 这图里生成的不对 但是步骤是对的。