目录
这里注意#ifndef,#define,#endif的作用和用法
1、实验内容:
通过HT11温湿传感器模块,检测温度和湿度,通过串口将数据传输到电脑,通过蓝牙无线透穿传输到 手机,通过lcd1602显示出来,然后可以根据温度对舵机的角度进行控制,也可以通过串口控制舵机
对于有模块忘记这么使用的可以去看我之前的文章
SG90舵机:STM32之定时器--PWM控制SG90舵机
lcd1602:C51单片机与LCD1602
DHT11:51单片机与DHT11温湿传感器
2、具体代码
1.Led1602显示屏操作代码
STM32如何将一个字节的数据按位一次性发送到GPIOA的8个管脚?
GPIOA -> ODR = cmd ;
lcd1602.c文件
#include "gpio.h"
#include "lcd1602.h"
#define RS_GPIO_Port GPIOB
#define RW_GPIO_Port GPIOB
#define EN_GPIO_Port GPIOB
#define RS_Pin GPIO_PIN_0
#define RW_Pin GPIO_PIN_1
#define EN_Pin GPIO_PIN_2
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_RESET)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_RESET)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET)
#define Data_0 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_0);
#define Data_1 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_1);
#define Data_2 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_2);
#define Data_3 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_3);
#define Data_4 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_4);
#define Data_5 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_5);
#define Data_6 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_6);
#define Data_7 HIGH HAL_GPIO_ReadPin(CPIOA,GPIO_PIN_7);
//这里如果你的引脚不是连续的你就可以通过按位传输的方式来给lcd显示屏传递你想要显示的数据
//void DHT11_Wdata(uint8_t Data)
//{
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,Data & 0x00000001);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,Data & 0x02);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,Data & 0x04);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,Data & 0x08);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,Data & 0x10);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,Data & 0x20);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,Data & 0x40);
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,Data & 0x80);
//}
void WriteOrder(char order)//写指令也就是输入字符的位置
{
//Detection_busy();
RS_LOW;
RW_LOW;
EN_LOW;
//DHT11_Wdata(order);
GPIOA->ODR = order;
HAL_Delay(1);
EN_HIGH;
HAL_Delay(1);
EN_LOW;
}
void WriteDate(char date)//写数据
{
//Detection_busy();
RS_HIGH;
RW_LOW;
EN_LOW;
//DHT11_Wdata(date);
GPIOA->ODR = date;
HAL_Delay(1);
EN_HIGH;
HAL_Delay(1);
EN_LOW;
}
void InitializeLcd1602(void)//lcd1602初始化过程
{
//(1)延时 15ms
HAL_Delay(15);
//(2)写指令 38H(不检测忙信号)
WriteOrder(0x38);
//(3)延时 5ms
HAL_Delay(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置
// Detection_busy();
WriteOrder(0x38);
//(6)写指令 08H:显示关闭
//Detection_busy();
WriteOrder(0x08);
//(7)写指令 01H:显示清屏
//Detection_busy();
WriteOrder(0x01);
//(8)写指令 06H:显示光标移动设置
//Detection_busy();
WriteOrder(0x06);
//(9)写指令 0CH:显示开及光标设置
// Detection_busy();
WriteOrder(0x0C);
}
void LCD1602_showLine(char row, char col, char *string)
{
switch(row)
{
case 1:
WriteOrder(0x80+col);
while(*string){
WriteDate(*string);
string++;
}
break;
case 2:
WriteOrder(0x80+0x40+col);
while(*string){
WriteDate(*string);
string++;
}
}
}
lcd1602.h文件
#ifndef __LCD1602_H__
#define __LCD1602_H__
void InitializeLcd1602(void);
void LCD1602_showLine(char row, char col, char *string);
#endif
这里注意#ifndef,#define,#endif的作用和用法
ifndef/define/endif”主要目的是防止头文件的重复包含和编译
用法:
.h文件,如下:
#ifndef __XX_H__
#define __XX_H__
...
#endif这样如果有两个地方都包含这个头文件,就不会出现两次包含的情况
因为在第二次包含时 XX_H 已经有定义了,所以就不再 include了
2.DHt11温湿度传感器操作代码
dht11.c文件
#include "dht11.h"
#include "gpio.h"
//将GPIO口的操作进行封装
#define DHT_HIGHT HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET)
#define DHT_LOW HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET)
#define DHT_VALUE HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)
//接收传递过来的数据
char datas[5];
void delay_us(uint16_t cnt)
{
uint8_t i;
while(cnt)
{
for (i = 0; i < 10; i++)
{
}
cnt--;
}
}
//GPIO的初始化
void DHT11_GPIO_Init(uint32_t mode)
{
/*mode
GPIO_MODE_INPUT
GPIO_MODE_OUTPUT_PP
*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = mode;
// GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//检测信号代码
void DHT11_DSR()
{
DHT11_GPIO_Init(GPIO_MODE_OUTPUT_PP);
DHT_HIGHT;
//HAL_Delay(1);
DHT_LOW;
HAL_Delay(30);
DHT_HIGHT;
DHT11_GPIO_Init(GPIO_MODE_INPUT);
while(DHT_VALUE);
while(!DHT_VALUE);
while(DHT_VALUE);
}
//读取数据
void DHT11TransferData(void)
{
int i = 0;
int j = 0;
char cmd = 0;
DHT11_DSR();
for(i = 0;i<5 ;i++)
{
for(j = 0;j<8;j++)
{
while(!DHT_VALUE);
delay_us(40);
if(DHT_VALUE == 1)
{
cmd <<=1;
cmd |= DHT_VALUE;
while(DHT_VALUE);
}else
{
cmd <<=1;
cmd |= DHT_VALUE;
}
}
datas[i] = cmd;
}
}
dht11.h文件
#ifndef __DHT11_H__
#define __DHT11_H__
void DHT11TransferData(void);
#endif
3.串口文件,重定向printf
记得打开Use MicroLIB
#include "stdio.h"
int fputc(int ch, FILE *f)
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}
4.主函数代码
#include "lcd1602.h"
#include "dht11.h"
#include "stdio.h"
#include "string.h"
//舵机状态控制
#define OFF_1 1
#define OFF_2 2
#define ON_0 0
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
//串口接收缓存(1字节)
uint8_t buf=0;
//接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//属于接收数据
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART1 || huart->Instance == USART3){
// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
if((UART1_RX_STA & 0x8000) == 0)
{// 如果已经收到了 0x0d (回车),
if(UART1_RX_STA & 0x4000)
{
// 则接着判断是否收到 0x0a (换行)
if(buf == 0x0a)
{
// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
UART1_RX_STA |= 0x8000;
if(!strcmp((char*)UART1_RX_Buffer,"OPEN_S")){
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,25);
}
if(!strcmp((char*)UART1_RX_Buffer,"OPEN_W")){
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,5);
}
if(!strcmp((char*)UART1_RX_Buffer,"CLOSE")){
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,15);
}
memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
UART1_RX_STA = 0;
}
else
{
// 否则认为接收错误,重新开始
UART1_RX_STA = 0;
}
}
else// 如果没有收到了 0x0d (回车)
{
//则先判断收到的这个字符是否是 0x0d (回车)
if(buf == 0x0d)
// 如果是则把bit14位置为1
UART1_RX_STA |= 0x4000;
else
{
//如果不是则将缓冲区里的数据放入接收缓冲里
UART1_RX_Buffer[UART1_RX_STA & 0x3fff] = buf;
UART1_RX_STA++;//这里传输一次就会加一,一个可以传输2的13次方个字节
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
if(UART1_RX_STA > UART1_REC_LEN - 1)
UART1_RX_STA = 0;
}
}
}
// 重新开启中断。调用 这个函数的时候会关闭中断,存储完一个数据的时候得重新开启中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
}
}
//main里面
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
//串口
HAL_UART_Receive_IT(&huart1,&buf,1);
HAL_UART_Receive_IT(&huart3,&buf,1);
//PWM
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,15);
i = ON_0;
InitializeLcd1602();
// LCD1602_showLine(1, 2, "wqaetg");
// LCD1602_showLine(2, 2, "asdghd");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//这里一定要加延时不然温湿传感器会卡住
HAL_Delay(1000);
DHT11TransferData();
//先判断
if(datas[2]>25){
if(i != OFF_2){
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,5);
i = OFF_2;
}
}
else if(datas[2]>25 && datas[0] >90 ){
if(i != OFF_1){
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,25);
i = OFF_1;
}
}else{
if(i != ON_0){
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,15);
i = ON_0;
}
}
//把数据传给蓝牙
memset(Tdata,'\0',12);
sprintf(Tdata,"S %d.%d",datas[0],datas[1]);
HAL_UART_Transmit(&huart3,(uint8_t*)Tdata,sizeof(Tdata),100);
//把数据在lcd显示
LCD1602_showLine(1,0,Tdata);
memset(Tdata,'\0',12);
sprintf(Tdata,"W %d.%d",datas[2],datas[3]);
HAL_UART_Transmit(&huart3,(uint8_t*)Tdata,sizeof(Tdata),100);
//把数据在lcd显示
LCD1602_showLine(2,0,Tdata);
//把数据传给串口
printf("湿度:%d.%d\r\n",datas[0],datas[1]);
printf("温度:%d.%d\r\n",datas[2],datas[3]);
}