文章目录

  • 项目介绍`
  • 一、项目设计
  • 硬件设计:
  • 软件设计
  • 1.配置cubeMX
  • 7时钟配置
  • 8多文件
  • 9生成代码
  • 10将printf函数和scanf函数进行重定义。
  • 11USART2发送和中断接受功能。
  • 12命令发送接受函数,数据发送函数
  • 13WIFI连接
  • 14门铃报警
  • 15控制台灯亮灭
  • 总结



项目介绍`

在物联网无限通讯技术中,有着例如lora、ook、WIFI无线通讯等技术。他们有着各自的优缺点,本次实验中,采用的是WIFI无线通讯,连接单片机和手机。能够通过手机上的微信小程序来控制单片机上LED灯的亮灭,在单片机上按下按键,也能实现门铃报警功能。


一、项目设计

硬件设计:

1:单片机是韦东山的STM32F103MINI开发板
2:无线WIFI模块是乐鑫ESP8266

软件设计

本次项目,使用cubeMX和MDK两个软件完成。
cubeMX是意法半导体推荐的单片机图形化配置软件,通过这个软件开发项目,能够大大节省开发过程中浪费在配置过程中的时间,减少项目的开发周期,提高效率。而cubeMX所生成的代码是通过HAL库实现的,HAL库对比标准库而言,封装性更高,可移植性更高。这也是现在意法半导体主推的开发方式。

1.配置cubeMX

打开cubeMX,根据硬件原理图进行配置。

1配置PA0,上升沿触发中断。可以在GPIO→User Label中写上标签KEY,这样在配置完成后,PA0的会被重命名为KEY,方便使用,这里忘了写。

2配置PA1推挽输出,标签为LED。

3配置PD1和PD0为连接外部高速晶振HSE的引脚,提供时钟。

4配置PA14和PA13为调试引脚,连接ST-LINK。

5配置PA9和PA10为USART1_RX和USART1_TX,这里主要用来串口输出信息。

6配置PA2和PA3为USART2_TX和USART2_RX,配置中断,这里用来连接WIFI模块。

stm32 esp8266控制开关_stm32 esp8266控制开关

7时钟配置

当所有需要的引脚配置完毕后,进入Clock Configuration配置界面,可以看到HSE默认为8MHz,不用做修改。点击HCLK,输入72之后回车,软件会自动配置前面的分频因子,得到结果。将挂载在APB1和总线配置为36MHz,APB2总线上的外设配置为72MHz的系统时钟。

stm32 esp8266控制开关_stm32 esp8266控制开关_02

8多文件

在Code Generator中勾选Generate peripheral initialization sa apair of c/h files per peripheral 这个选项,对不同外设生成多个源文件和头文件。

stm32 esp8266控制开关_stm32_03

9生成代码

选择MDK-ARM,点击生成,之后可以用MDK打开,开始写之后的代码实现功能。

stm32 esp8266控制开关_stm32_04

10将printf函数和scanf函数进行重定义。

在usart.c文件中,将printf函数和scanf函数进行重定义,以便在串口工具中输入输出。这里必须要记住的是,所有的代码必须在注释中/* USER CODEBEGIN*/ 和/* USER CODE END 1 */之间的地方进行编写,之后如果还想要对项目修改,再次用cubemx生产配置之后,写在其他地方的代码会被删除,只有在规定地方写的才会保留。

/*****************************************************
*function: 写字符文件函数
*param1: 输出的字符
*param2: 文件指针
*return: 输出字符的 ASCII 码
******************************************************/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 10);
return ch;
}

/*****************************************************
*function: 读字符文件函数
*param1: 文件指针
*return: 读取字符的 ASCII 码
******************************************************/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, (uint8_t*)&ch, 1, 10);
return (int)ch;
}

#pragma import(__use_no_semihosting)
struct __FILE
{
int a;
};
FILE __stdout;
FILE __stdin;
void _sys_exit(int x)
{ 
}
static uint8_t Rx_Data=0;
static uint8_t buff[200];
static uint8_t newbuff[200];
//static uint8_t tx_buff[200]={0};
static uint8_t len=0;

11USART2发送和中断接受功能。

在HAL库中,hsart2的中断回调函数是一个虚函数,意思是说,如果用户没有定义则会运行原本的函数,但如果用户重新定义了该函数,则会运行用户写的。这里要把他复制过来重新定义,这样触发中断后会运行重新写的函数。

static uint8_t Rx_Data=0;
static uint8_t buff[200];
static uint8_t newbuff[200];
//static uint8_t tx_buff[200]={0};
static uint8_t len=0;


//开启接受中断
void rx_start(void)
  {
    HAL_UART_Receive_IT(&huart2,(uint8_t*)&Rx_Data,1);
  }

//中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  buff[len%200]=Rx_Data;
  len++;
  HAL_UART_Receive_IT(&huart2,(uint8_t*)&Rx_Data,1);
}
//清空buf和len
void clearbuffer()
{
  memset(buff,0,sizeof(buff));
  len=0;
}

//将接受数据赋值
uint8_t huart2_Rx(uint8_t *data)
{
  memcpy(data,buff,len);
  return len;
}

// 串口2发送数据
void usart2_Transmit(uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  clearbuffer();
	HAL_UART_Transmit(&huart2, pData, Size, Timeout);
}

12命令发送接受函数,数据发送函数

命令发送函数中,对输入的代码进行处理,利用strstr给他加上\r\n,然后发送出去再接收。接收的时候,利用sys进行延时,防止接受不全的情况出现。在查看ESP8266的数据手册的时候,能看到收到命令之后他会返回OK,判断单片机收到的数据中有没有OK,如果有就说明发送成功,没有就是失败。

uint8_t Cmd_Transmit(char *cmd, char *judge, uint32_t Timeout)
{
  
  //输入AT命令后面加入\r\n
  char buf[256]={0};
  strcat(buf,cmd);
  if(strstr(buf,"\r\n")==0)
  {
    strcat(buf,"\r\n");
  }
  usart2_Transmit(buf, strlen(buf),  500);

  memset(buf, 0, 256);
	while(Timeout != 0)
	{
		if(huart2_Rx((uint8_t *)buf))
		{
			if (strstr(buf, judge))
			{
        // 发送成功
				printf("%s Send ok!\r\n", cmd); 
				
				if (strstr(buf, "CIFSR"))
        {
					printf("%s\r\n", buf); // 打印IP地址
        }
				return 0;
			}
			else
			{
				Timeout--;
				HAL_Delay(1);
			}
		}
	}

	printf("%s Send error!\r\n", cmd); // 发送失败
	return 1;
}

uint8_t Data_Transmit(char *data)
{
  	// 1.准备发送的指令
	char buf[256] = {0};
	uint8_t len = strlen(data);
	sprintf(buf, "AT+CIPSEND=0,%d\r\n", len);
	// 2.发送指令
	if (Cmd_Transmit(buf, "OK", 500) == 0)
	{
		// 3.发送数据
		Cmd_Transmit(data, "OK", 1000);
		return 0;
	}
	return 1;
}

13WIFI连接

ESP8266可以使用TCP、UDP或者透传模式进行无线通讯,这里使用的是TCP,当然也可以用UDP。
主函数中,首先要调用一次 HAL_UART_Receive_IT函数,否则不会进入中断。然后开始发送命令。
之后打开开发板商提供的微信小程序,输入在串口工具上显示的ip地址,和端口9999,进行绑定。注意手机要连在同一个wifi信号下。

rx_start();
  Cmd_Transmit("AT+RST\r\n", "OK",1500);//重启模块
  HAL_Delay(1000);
 Cmd_Transmit("AT+CWMODE=1", "OK", 500);//设置sta模式
 Cmd_Transmit("AT+CWJAP=\"dadada\",\"1941025788\"", "OK", 5000);//输入wifi账号密码
 Cmd_Transmit("AT+CIPMUX=1","OK", 500);//设置多连接模式
 Cmd_Transmit("AT+CIPSERVER=1,9999", "OK", 500);//设置端口
 Cmd_Transmit("AT+CIFSR", "OK", 500);//查询模块IP

stm32 esp8266控制开关_单片机_05

14门铃报警

在gpio.c中,写下有案件的中断回调函数和获取按键状态的函数。
在main.c中判断按键状态,如果按键按下,就发送一段数据。注意的是在主函数中写在while(1)的循环中,这样可以不断轮询按键状态。
这时候按下按键,就会发现,微信小程序上的门铃按钮变成红色,代表受到了报警。

uint8_t Key_GetFlag(void)
{
	if(key_flag)
	{
		key_flag = 0;
		return 1;
	}
	else
		return 0;
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin ==GPIO_PIN_0)
	{
		key_flag = 1;
		//HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, key_flag?GPIO_PIN_RESET:GPIO_PIN_SET);
	}
}
if (Key_GetFlag() == 1)
	{
		Data_Transmit("{\"data\":\"doorbell\",\"status\":\"1\"}");
	}

15控制台灯亮灭

加入这段代码之后,在手机上点击台灯按钮,就会发现单片机上的led亮灭了。

if (huart2_Rx(rx_data)) //接收到数据
	{
		if (strstr((char *)rx_data, "\"dev\":\"led\",\"status\":\"0\""))
		{
			printf("led off\n\r");
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
			clearbuffer();
			memset(rx_data, 0, 200);
		}
		else if (strstr((char *)rx_data, "\"dev\":\"led\",\"status\":\"1\""))
		{
			printf("led on\n\r");
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
			clearbuffer();
			memset(rx_data, 0, 200);
		}
	}

总结

根据开发板提供的资料来说,还可以使用UDP和透传的方法进行无线通讯,实现起来都是大同小异的。这个小项目中可以修改的地方还有很多,比如说增加wifi账号和密码修改,否则换一个wifi就要重新写代码了。