基于STM32(Freertos)&ESP01S的红外空调网络控制器 -STM32软件部分

1.概述
本文主要介绍控制器软件部分,包含Freertos任务控制逻辑、Freertos系统初始化,STM32红外接收发射任务应用实现,加湿器模块控制实现、串口初始化。

2.主函数控制逻辑

在主函数中创建了4个任务,分别为:

  • 1.主任务管理器任务(Taskmgr)
  • 2.发送信息任务(SentMessage)
  • 3.空调控制任务(IR)
  • 4.加湿器控制任务(Humi)
    控制系统流程图如下:

    main.c 代码如下:
#include "stm32f10x.h"
#include "system.h"
#include "stdio.h" 
/*APP Doc*/
#include "HDC1080.h"
#include "oled.h"
#include "IRLED.h"
#include "Humidifier.h"
#include "Led.h"
/*FreeRtos element*/
#define Start_Task_Prio 5//任务优先级,数值越大优先级越高
#define Start_Stk_Size 100//任务堆栈大小,单位Word,4字节
TaskHandle_t StartTask_Handler;
void Start_Task(void* pvParameters);//函数未定义,先声明

#define Taskmgr_Task_Prio 2//任务优先级,数值越大优先级越高
#define Taskmgr_Stk_Size 100//任务堆栈大小,单位Word,4字节
TaskHandle_t Taskmgr_Handler;
void Taskmgr_Task(void* pvParameters);//函数未定义,先声明

#define SentMessage_Task_Prio 4//任务优先级,数值越大优先级越高
#define SentMessage_Stk_Size 100//任务堆栈大小
TaskHandle_t SentMessageTask_Handler;
void SentMessage_Task(void* p_arg);//函数未定义,先声明

#define IR_Task_Prio 3//任务优先级,数值越大优先级越高
#define IR_Stk_Size 100//任务堆栈大小
TaskHandle_t IR_Task_Handler;
void IR_Task(void* p_arg);//函数未定义,先声明

#define Humi_Task_Prio 3//任务优先级,数值越大优先级越高
#define Humi_Stk_Size 100//任务堆栈大小
TaskHandle_t Humi_Task_Handler;
void Humi_Task(void* p_arg);//函数未定义,先声明

//#define debug_Task_Prio 3//任务优先级,数值越大优先级越高
//#define debug_Stk_Size 100//任务堆栈大小
//TaskHandle_t debug_Task_Handler;
//void debug_Task(void* p_arg);//函数未定义,先声明
//全局变量
extern volatile uint8_t ir_data[6];//供接收数据存储,未初始化接收时无用

SysData SYS_Glabal_varible={0,0,State_OFF,{0,Auto,26}};

int main(void)
{
  SYSCLK_Config();
	delay_Init(ManualSetSYSCLK);
/*
  //必须尽早创建任务,不然SCKtick进入中断调用xTaskIncrementTick后会造成野指针,
	//具体内容:https://blog.csdn.net/zhjmyx/article/details/120490993
	//创建任务
  //创建开始任务,此任务在执行后被删除
*/	
	xTaskCreate((TaskFunction_t) Start_Task,//任务函数
							(const char*)"Start_task",//任务名称
						  (uint16_t) Start_Stk_Size,//任务堆栈大小
							(void*)NULL,          //传递给任务函数的参数
							(UBaseType_t)Start_Task_Prio,//任务优先级
							(TaskHandle_t*) &StartTask_Handler);
//通信初始化	
	USART1_Init(9600);
	USART3_Init(9600);
  IIC_Init();
//App 初始化
	Ledflow();
	HDC_Init();
	IR_Init();//默认发射配置
	RH_GPIO_Init();
//	OLED_Init();
//  OLED_ColorTurn(0);
//	OLED_DisplayTurn(0);

  vTaskStartScheduler();//此函数对调用系统Systick初始化,覆盖掉前序配置
}

void Start_Task(void* pvParameters)
{
	taskENTER_CRITICAL();//进入临界区,在此区域的代码不会被打断
	//创建IIC通信相关任务,将IIC功能放在不同的任务中,会导致相互竞争IIC控制权
							
	
							xTaskCreate((TaskFunction_t) SentMessage_Task,//任务函数
							(const char*)"HDC_task",//任务名称
						  (uint16_t) SentMessage_Stk_Size,//任务堆栈大小
							(void*)NULL,          //传递给任务函数的参数
							(UBaseType_t)SentMessage_Task_Prio,//任务优先级
							(TaskHandle_t*) &SentMessageTask_Handler);
		
							xTaskCreate((TaskFunction_t) Humi_Task,//任务函数
							(const char*)"Humi_task",//任务名称
						    (uint16_t)Humi_Stk_Size,//任务堆栈大小
							(void*)NULL,          //传递给任务函数的参数
							(UBaseType_t)Humi_Task_Prio,//任务优先级
							(TaskHandle_t*) &Humi_Task_Handler);
							
							xTaskCreate((TaskFunction_t) IR_Task,//任务函数
							(const char*)"IR_task",//任务名称
						    (uint16_t)IR_Stk_Size,//任务堆栈大小
							(void*)NULL,          //传递给任务函数的参数
							(UBaseType_t)IR_Task_Prio,//任务优先级
							(TaskHandle_t*) &IR_Task_Handler);
							
							xTaskCreate((TaskFunction_t) Taskmgr_Task,//任务函数
							(const char*)"Taskmgr_Task",//任务名称
						  (uint16_t) Taskmgr_Stk_Size,//任务堆栈大小
							(void*)NULL,          //传递给任务函数的参数
							(UBaseType_t)Taskmgr_Task_Prio,//任务优先级
							(TaskHandle_t*) &Taskmgr_Handler);

//							xTaskCreate((TaskFunction_t) debug_Task,//任务函数
//							(const char*)"debug_Task",//任务名称
//						  (uint16_t) debug_Stk_Size,//任务堆栈大小
//							(void*)NULL,          //传递给任务函数的参数
//							(UBaseType_t)debug_Task_Prio,//任务优先级
//							(TaskHandle_t*) &debug_Task_Handler);
							
		vTaskDelete(StartTask_Handler);	
  taskEXIT_CRITICAL();								
}
void Taskmgr_Task(void* p_arg)
{
  u8 lastIR_Temp,lastIR_mode,last_humimode;
	u8 OFF_Sta;
	while(1)
	{
		
 		if((lastIR_Temp!=USART3_RX_BUF[4])||(lastIR_mode!=USART3_RX_BUF[5]))
		{
			//IR_命令有变,执行命令
			lastIR_Temp=USART3_RX_BUF[4];//更新最后一次的IR控制值
	        lastIR_mode=USART3_RX_BUF[5];
			OFF_Sta=0;//其他途径开机
			xTaskNotifyGive(IR_Task_Handler); 
		}
		if(last_humimode!=USART3_RX_BUF[6])
		{			
	        last_humimode=USART3_RX_BUF[6];
			xTaskNotifyGive(Humi_Task_Handler);
		}
		if(((USART3_RX_BUF[7]&0x0F)==0x0F)&&OFF_Sta==0)//低四位变F时关机
		{
			  USART3_RX_BUF[7]|=0xF0;//低四位清零
			  OFF_Sta=1;
			  Normal_Code(0xB2,0x7B,0xE0);//关机
		}
		else if((USART3_RX_BUF[7]==0x00)&&(OFF_Sta==1))
    {
			//否则为开机,执行上一次的空调控制参数
			xTaskNotifyGive(IR_Task_Handler);
			OFF_Sta=0;
		}
		
	}
}
void SentMessage_Task(void* pvParameters)//温湿度读取任务
{
      float temp,humid;
	    u32 NotifyVaule;
		  while(1)
    	{
			NotifyVaule=ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
				if(NotifyVaule==1)
				{
			
			//获取传感器数据
			Get_HDC1080_THValue(&temp,&humid);
			SYS_Glabal_varible.curtemp=temp;
			SYS_Glabal_varible.curhumid=humid;
			//0.发送0xB3
            USART_SendData(USART3,0xB3);
			delay_ms(100);//延时必须,ESP端使用状态机接收,太快了处理不过来
            //1.发送空调温度	
            USART_SendData(USART3,SYS_Glabal_varible.IR_Sent_Data.temp);
			delay_ms(100);
		    //2.发送空调模式
		    USART_SendData(USART3,SYS_Glabal_varible.IR_Sent_Data.mode);
			delay_ms(100);
  		    //3.发送加湿器模式
		    USART_SendData(USART3,SYS_Glabal_varible.RH_Sta);
			delay_ms(100);
   		    //4-7.发送温度数据
		    send_float_via_uart(USART3,temp);		
   		    //8-11.发送湿度数据
		    send_float_via_uart(USART3,humid);
			delay_ms(100);
				}
	    }		
}
void IR_Task(void* p_arg)
{
	u32 NotifyVaule;
	while(1)
	{
		NotifyVaule=ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        GPIO_SetBits(GPIOA, GPIO_Pin_5);		
        if(NotifyVaule==1)
		{
		ConducterControl((ConducterMode)USART3_RX_BUF[5] ,USART3_RX_BUF[4]);//第五byte空调温度,第六byte空调模式
		xTaskNotifyGive(SentMessageTask_Handler);
		}
		
	}
}
void Humi_Task(void* p_arg)
{ 
  u32 NotifyVaule;
	while(1)
	{
		 NotifyVaule=ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
		 if(NotifyVaule==1)
		 {			 
		 RH_Control((RH_State)USART3_RX_BUF[6]);
		 xTaskNotifyGive(SentMessageTask_Handler);
		 }
	}
}
void debug_Task(void* p_arg)
{
	while(1)
	{
	  delay_ms(1000);
	  Decode_IRDta();
	  printf(" A=%#X,NA=%#X", ir_data[0],ir_data[1]);
	  printf(" B=%#X,NB=%#X", ir_data[2],ir_data[3]);
	  printf(" C=%#X,NC=%#X", ir_data[4],ir_data[5]);
	}
}

3.Freertos 初始化

3.1Freertos 配置

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 
 *
 * See http://www.freertos.org/a00110.html
 *----------------------------------------------------------*/

#define configUSE_PREEMPTION		1//设置抢占式调度器
#define configUSE_IDLE_HOOK			0//设置空闲任务钩子
#define configUSE_TIME_SLICING  0//设置相同优先级任务被轮转执行
#define configUSE_TICK_HOOK			0//设置时间片钩子
#define configUSE_TASK_NOTIFICATIONS 1
#define configUSE_MUTEXES 1

#define configPRIO_BITS    4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   0x0F //四位,值越小优先级越高

#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )	//CPU执行频率
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )//节拍中断频率,每次中断会进行任务调度
#define configMAX_PRIORITIES		( 5 )//设置优先级范围:0·X-1数字越小优先级越低
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 256 )//定义空闲任务使用的堆栈的大小
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 10 * 1024 ) )//
#define configMAX_TASK_NAME_LEN		( 16 )//定义任务名的长度限制
#define configUSE_TRACE_FACILITY	0//启动可视化跟踪调试
#define configUSE_16_BIT_TICKS		0
#define configIDLE_SHOULD_YIELD		1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
//#define configKERNEL_INTERRUPT_PRIORITY 		255 //在port.c 中L37定义
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */

#define xPortPendSVHandler   PendSV_Handler
#define xPortSysTickHandler  SysTick_Handler
#define vPortSVCHandler      SVC_Handler



/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
//#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15		//数字越小优先级越高,这里高于"最低优先级15"都不可管理
//即只可管理最低15优先级,其他都管不了,不合理我要改
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	  5		//数字越小优先级越高,这里高于5都不可管理,即数字小于5的都管不了 
#endif /* FREERTOS_CONFIG_H */

3.2 系统初始化

此处是System.h的代码,主要定义全局变量及I2C的引脚宏定义

#ifndef __system_H
#define __system_H
/*System Head doc*/
#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "usart.h"
#include "iic.h"

/*FreeRtos lib*/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
 typedef enum
{
		State_OFF,
		State_Low,
		State_High,
}RH_State;
typedef enum {Auto,cold,DeHumi,Heat,SentWind}ConducterMode;
//RH_State RH_app_Sta = State_OFF;
typedef struct IR_S
{
	u8 IR_Sent_Data[3];//依次A B C,A固定0xB2,B的高3位控制风速,低5五位保留1,
	  //高3位101为自动风,本程序风速默认自动风
	ConducterMode mode;
	u8 temp;
}IR_Sdate;
typedef struct SystemData
{
	   float curtemp;
	   float curhumid;
	   RH_State RH_Sta;
	   IR_Sdate IR_Sent_Data;
}SysData;


extern SysData SYS_Glabal_varible;
extern IR_Sdate IR_SentData;

#define SYSTEM_SUPPPORT_RTOS 1//FRTOS开关标准位

/*  IIC_SCL时钟端口、引脚定义 */
#define IIC_SCL_PORT 			GPIOB   
#define IIC_SCL_PIN 			(GPIO_Pin_8)
#define IIC_SCL_PORT_RCC		RCC_APB2Periph_GPIOB

/*  IIC_SDA时钟端口、引脚定义 */
#define IIC_SDA_PORT 			GPIOB  
#define IIC_SDA_PIN 			(GPIO_Pin_9)
#define IIC_SDA_PORT_RCC		RCC_APB2Periph_GPIOB

#define ManualSetSYSCLK		72

//#define IIC_SCL *((volatile unsigned long*)(0x42000000+((GPIOB_BASE+12)-0x40000000)*32+8*4))//写PB8
//#define IIC_SDA *((volatile unsigned long*)(0x42000000+((GPIOB_BASE+12)-0x40000000)*32+9*4))//写PB9
//#define PB10 *((volatile unsigned long*)(0x42000000+((GPIOB_BASE+12)-0x40000000)*32+10*4))//写PB10
//#define READ_SDA  *((volatile unsigned long*)(0x42000000+((GPIOB_BASE+8)-0x40000000)*32+9*4))//读PB9
//unsigned short int returntimeline(void);
void SYSCLK_Config(void);
#endif 

系统时钟及中断组选择

/*
功能:系统初始配置
参数:无
说明:系统在执行main之前会执行SystemInit,其中包含时钟的初始化,但确保初始化成功,此处手动进行时钟及中断组选择
*/
void SYSCLK_Config(void)
{
	RCC_DeInit();
	RCC_HSEConfig(RCC_HSE_ON);
	if (RCC_WaitForHSEStartUp() == SUCCESS)
    {
        // 关闭HSI(可选)
        RCC_HSICmd(DISABLE);
        
        // 配置PLL:HSE分频1(不分频)后作为PLL输入,倍频9倍
        // 注意:根据具体型号选择分频系数,此处为STM32F1系列典型配置
        RCC_PLLConfig(RCC_PLLSource_HSE_Div2, RCC_PLLMul_9);
        
        // 启动PLL并等待就绪
        RCC_PLLCmd(ENABLE);
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        
        // 切换系统时钟到PLL输出
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        
        // 验证时钟切换是否成功
        while(RCC_GetSYSCLKSource() != 0x08);
        
        // 配置总线分频(关键修改点)
        RCC_HCLKConfig(RCC_SYSCLK_Div1);    // AHB 72MHz
        RCC_PCLK1Config(RCC_HCLK_Div2);     // APB1 36MHz(不超过官方限制)
        RCC_PCLK2Config(RCC_HCLK_Div1);     // APB2 72MHz
				
				vPortRaiseBASEPRI();//开启Freertos的中断管理
				NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
				
    }
}

3.3 系统delay及滴答定时器初始化

#include "delay.h" 
u8 fac_us,fac_ms;

/*******************************************************************************
* 函数名      : delay_Init
* 功能		    : 对系统滴答定时器初始化
* 参数        : SYSCLK,已设置的系统时钟频率如:72,其已在启动文件中设置
* 返回值      : 无
* 描述        : 无
*******************************************************************************/ 
void delay_Init(u8 SYSCLK)
{
	u32 reload;
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//设置为HCLK不分频,本机在stm32f10x_system.c文件中定义了36MHZ系统时钟频率
	fac_us=SYSCLK;//每微秒需要计的数量,nMHZ执计n次为微秒
	reload=SYSCLK;
	reload=(reload*1000000/configTICK_RATE_HZ);
	//先乘以1000000恢复Mhz,再除以OS工作频率,得到计数次数,
	//按此重装值计数的中断可得到想要的OS工作频率
	fac_ms=1000/configTICK_RATE_HZ;//t=1/f(秒)
	//系统频率下的最小单位时间,乘以1000因为要的是毫秒
	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
	SysTick->LOAD=reload;//每1/configTICK_RATE_HZ秒中断一次
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;//开启SYSTICK
}
/*******************************************************************************
* 函数名      : delay_us
* 功能		    : 延时nus
* 参数        : nus,要延时的微秒数
* 返回值      : 无
* 描述        : 因为OS的工作频率设定为1000,最小时间单位1ms,
                无法在使用OS框架内延时us级时间
*******************************************************************************/ 
void delay_us(uint32_t nus)
{
	u32 ticks;
	u32 told ,tnow,tcnt=0;
	u32 reload=SysTick->LOAD;//读取当前重装LOAD值
	ticks=(nus*fac_us);//需要的节拍数
	told=SysTick->VAL;//进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;//读取当前值
		if(tnow!=told)//除非单片机卡死了,old值和now值没变化,那就卡死在while里等待恢复
		{
			if(tnow<told)
			{
				tcnt+=told-tnow;//记录两次间隔的计数数量并累加
			}
			else//为特殊情况now值到0重装后突变的增加的重装值做修正
			{
				tcnt+=(told-(tnow-reload));
			}
			told = tnow;//更新Old值
			if(tcnt>=ticks)
				break;//时间等于或超过要延时的时间,退出while
		}
	}
}
/*******************************************************************************
* 函数名      : delay_ms
* 功能		    : 延时nms
* 参数        : nms,要延时的毫秒数
* 返回值      : 无
* 描述        : 会引起任务调度
*******************************************************************************/ 
void delay_ms(uint32_t nms)
{
//	if(1/*xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED*/)
		if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)
	{
		if(nms>=fac_ms)
		{
			vTaskDelay(nms/fac_ms);//整数倍的毫秒使用任务调度延时
		}
		nms%=fac_ms;		
	}
	delay_us((u32)(nms*1000));//余下部分化成微秒用普通微秒延时
}
/*******************************************************************************
* 函数名      : delay_ms
* 功能		    : 延时nms
* 参数        : nms,要延时的毫秒数
* 返回值      : 无
* 描述        : 不会引起任务调度
*******************************************************************************/ 
void delay_xms(uint32_t nms)
{
	u32 i;
	for(i=0;i<nms;i++)
	delay_us(1000);
}
void Clock_Test(uint32_t ticks)
{

	u32 told ,tnow,tcnt=0;
	u32 reload=SysTick->LOAD;//读取当前重装LOAD值
//	ticks=(nus*fac_us);//需要的节拍数
	told=SysTick->VAL;//进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;//读取当前值
		if(tnow!=told)//除非单片机卡死了,old值和now值没变化,那就卡死在while里等待恢复
		{
			if(tnow<told)
			{
				tcnt+=told-tnow;//记录两次间隔的计数数量并累加
			}
			else//为特殊情况now值到0重装后突变的增加的重装值做修正
			{
				tcnt+=(told-(tnow-reload));
			}
			told=tnow;
			if(tcnt>=ticks)
				break;//时间等于或超过要延时的时间,退出while
		}
	}
}

4.红外控制

4.1 红外发射接收APP 预设参数

以下代码主要包含红外接收,各信号的时间长度宏定义,发送相关红外协议命令宏定义

#ifndef __IRLED_H
#define __IRLED_H
#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "system.h"

/* 红外接收相关定义 */
#define IR_TIMER               TIM3
#define IR_TIMER_CHANNEL       TIM_Channel_1
#define IR_GPIO_PORT           GPIOB
#define IR_GPIO_PIN            GPIO_Pin_4
#define IR_CLOCK_FREQ          36000000  // 系统时钟72MHz,此处我设置位36MHZ,红外接收的定时计数器每计一个数位为0.5微妙
#define IR_Lead_MAX            18500
#define IR_Lead_MIN            15000
#define IR_DATA_1_MAX          4500
#define IR_DATA_1_MIN          4100
#define IR_DATA_0_MAX          2300
#define IR_DATA_0_MIN          2000
#define SEPARATION_MAX         13000
#define SEPARATION_MIN         10000
//空调相关控制参数,R05D协议
#define IR_A_Data                   0xB2
#define IR_B_Windspeed_Auto         0xBF
#define IR_B_Windspeed_Low          0x9F
#define IR_B_Windspeed_Mid 		      0x5F
#define IR_B_Windspeed_High		      0x3F
#define IR_B_OFF                    0x7B
//工作模式只低占3-4位,高4位的温度区域先保留为0
#define IR_C_OperationMode_Auto			0x08
#define IR_C_OperationMode_cold			0x00
#define IR_C_OperationMode_DeHumi		0x04
#define IR_C_OperationMode_Heat		  0x0C
#define IR_C_OperationMode_SentWind	0x04 //送风模式没有温度代码
//温度控制参数,低4位都保留为0
#define IR_C_temp_17					0x00
#define IR_C_temp_18					0x10
#define IR_C_temp_19					0x30
#define IR_C_temp_20					0x20
#define IR_C_temp_21					0x60
#define IR_C_temp_22					0x70
#define IR_C_temp_23					0x50
#define IR_C_temp_24					0x40
#define IR_C_temp_25					0xC0
#define IR_C_temp_26					0xD0
#define IR_C_temp_27					0x90
#define IR_C_temp_28					0x80
#define IR_C_temp_29					0xA0
#define IR_C_temp_30					0xB0
#define IR_C_temp_Nop					0xE0
#define IR_C_OFF              0xE0
/* 时间数据buffer */

extern u16 IR_Buffer_Row[125];
void IR_TIM_Init(void);
u8 decode_A(u16* rowdata);
u8 decode_NA(u16* rowdata);
u8 decode_B(u16* rowdata);
u8 decode_NB(u16* rowdata);
u8 decode_C(u16* rowdata);
u8 decode_NC(u16* rowdata);
u8 Decode_IRDta(void);
u8  Lead_cheak(u16* rowdata);
u8 Separation_cheak(u16* rowdata);

	

void IR_Init(void);
void IR_TIM3_PWM_Init(void) ;
void IRLED_GPIO_Init(void);
void Normal_Code(u8 A, u8 B, u8 C);
void TIM3_SETLOW(void);
void TIM3_SETHIG(void);
void Lead_Code(void);
void Stop_Code(void);
void Send_0_Code(void);
void Send_1_Code(void);
void Send_Byte(u8 data);
u8 ConducterControl(ConducterMode mode ,u8 temp);
//     Normal_Code(0xB2, 0x9F, 0x00);//开机 17度
#endif

4.2 红外初始化函数

/*
功能:对红外发射接收功能进行初始
参数:无
说明:不可同时启用发射接收初始化,因为他们都使用TIM3定时器
*/
void IR_Init(void) 
	{
      IR_TIM3_PWM_Init();//红外发射功能PWM初始化
	  //IR_TIM_Init();//红外接收功能初始化
   }

4.3 红外发射控制。

通过初始化TIM3以38KHZ输出PWM信号,实现38khz载波,通过改变比较值,控制输出的是38Khz的PWM信号,还是低电平,以此实现发送高或发送低
实现代码如下,部分代码参考了:https://blog.csdn.net/weixin_42204837/article/details/109263771

/*
功能:PWM初始
参数:无
*/
void IR_TIM3_PWM_Init(void) 
	{
    // 使能GPIOA和TIM3时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    // 配置PA6为复用推挽输出(用于Q2栅极)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置TIM3
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 1894; // 38kHz PWM (72MHz / (947 + 1) = 38kHz)
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    // 配置TIM3通道1为PWM模式
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 947; // 947 50%占空比
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    // 启动TIM3
    TIM_Cmd(TIM3, ENABLE);
 }
/*
功能:红外不输出,接收器拉高,发送低
参数:无
*/
void 	TIM3_SETLOW(void) 
{
   TIM3->CCR1 = 0;
//TIM_SetCompare1(TIM3,0);
}
/*
功能:红外输出,接收器拉低,发送高
参数:无
*/
void 	TIM3_SETHIG() 
{

    TIM3->CCR1 = 947;
//		TIM_SetCompare1(TIM3,947);
}
/*
功能:发送美的红外控制引导码
参数:无
*/ 
void Lead_Code(void)
{
	TIM3_SETHIG(); //接收器拉低,对发送就是高
	delay_us(4400);
	TIM3_SETLOW(); //接收器拉高,对发送就是低
	delay_us(4400);
}
/*
功能:发送美的红外控制停止码
参数:无
*/ 
void Stop_Code(void)
{
	TIM3_SETHIG(); //接收器拉低
	delay_us(540);
	TIM3_SETLOW(); //接收器拉高
	delay_us(5220);
}
/*
功能:发送美的红外 bit 0
参数:无
*/
void Send_0_Code(void)
{
	TIM3_SETHIG(); //接收器拉低
	delay_us(540);
	TIM3_SETLOW(); //接收器拉高
	delay_us(540);
}
/*
功能:发送美的红外 bit 1
参数:无
*/
void Send_1_Code(void)
{
	TIM3_SETHIG(); //接收器拉低
	delay_us(540);
	TIM3_SETLOW(); //接收器拉高
	delay_us(1620);
}
/*
功能:发送美的红外8位byte
参数:无
*/
void Send_Byte(u8 data)
{
	int i;
	for(i=7;i>=0;i--)
	{
		if(data & (1<<i))
		{
			Send_1_Code();
		}
		else
		{
			Send_0_Code();
		}
	}
}
/*
功能:发送美的红外控制的一条完整信号
参数:无
*/
void Normal_Code(u8 A, u8 B, u8 C)
{
	Lead_Code();
	Send_Byte(A);
	Send_Byte(~A);
	Send_Byte(B);
	Send_Byte(~B);
	Send_Byte(C);
	Send_Byte(~C);
	Stop_Code();
	Lead_Code();
	Send_Byte(A);
	Send_Byte(~A);
	Send_Byte(B);
	Send_Byte(~B);
	Send_Byte(C);
	Send_Byte(~C);
	Stop_Code();
}
/*
功能:控制红外发射完成相应模式温度的信号发送
参数:mode 枚举变量,通过switch对模式进行选择
     temp 通过switch对温度进行选择
*/
u8 ConducterControl(ConducterMode mode ,u8 temp)
{
	u8 C_Data,ModeData,TempData;
	switch(mode)
  {
		case Auto     :ModeData=IR_C_OperationMode_Auto;break;
		case cold     :ModeData=IR_C_OperationMode_cold;break;
		case DeHumi   :ModeData=IR_C_OperationMode_DeHumi;break;
		case Heat     :ModeData=IR_C_OperationMode_Heat;break;
		case SentWind :ModeData=IR_C_OperationMode_SentWind;break;
		default: return 0;
	}
	switch(temp)
	{
		case 17 : TempData=IR_C_temp_17;break;
		case 18 : TempData=IR_C_temp_18;break;
		case 19 : TempData=IR_C_temp_19;break;
		case 20 : TempData=IR_C_temp_20;break;
		case 21 : TempData=IR_C_temp_21;break;
		case 22 : TempData=IR_C_temp_22;break;
		case 23 : TempData=IR_C_temp_23;break;
		case 24 : TempData=IR_C_temp_24;break;
		case 25 : TempData=IR_C_temp_25;break;
		case 26 : TempData=IR_C_temp_26;break;
		case 27 : TempData=IR_C_temp_27;break;
		case 28 : TempData=IR_C_temp_28;break;
		case 29 : TempData=IR_C_temp_29;break;
		case 30 : TempData=IR_C_temp_30;break;
		default : TempData=IR_C_temp_Nop;break;
	}
	C_Data=(ModeData&0x0C)|(TempData&0xF0);
	SYS_Glabal_varible.IR_Sent_Data.IR_Sent_Data[0]=0xB2;
	SYS_Glabal_varible.IR_Sent_Data.IR_Sent_Data[1]=IR_B_Windspeed_Auto;
	SYS_Glabal_varible.IR_Sent_Data.IR_Sent_Data[2]=C_Data;
	SYS_Glabal_varible.IR_Sent_Data.temp=temp;
	SYS_Glabal_varible.IR_Sent_Data.mode=mode;
	Normal_Code(0xB2,IR_B_Windspeed_Auto,C_Data);
	return 1;
}

4.4 红外接收实现

红外接收通过定时器输入捕获每个下降沿间的时间差,将其存储在IR_Buffer_Row[]中,然后再IR_Buffer_Row[]存储完成时对数据进行解码

/*
功能:接收捕获的定时器配置
参数:无
*/
void IR_TIM_Init(void) {
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 开启时钟 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/* 开启AFIO时钟(关键!)*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/* 配置PB4复用为TIM3_CH1 */
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);  // 部分重映射:TIM3_CH1->PB4

    /* 配置GPIO */
    GPIO_InitStructure.GPIO_Pin = IR_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 
    GPIO_Init(IR_GPIO_PORT, &GPIO_InitStructure);

    /* 定时器基础配置 */
	//APB1总线上的定时器时钟确实有一个倍频机制。当APB1的预分频系数设置为1以外的值时(即分频系数为2、4、8或16),定时器的时钟频率会是APB1频率的两倍
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = (IR_CLOCK_FREQ/1000000) - 1; // 0.5us计数
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(IR_TIMER, &TIM_TimeBaseStructure);

    /* 输入捕获配置 */
    TIM_ICInitStructure.TIM_Channel = IR_TIMER_CHANNEL;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; // 双沿触发
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x08; // 适当滤波
    TIM_ICInit(IR_TIMER, &TIM_ICInitStructure);

    /* 中断配置 */
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_ClearITPendingBit(IR_TIMER, TIM_IT_CC1 | TIM_IT_Update);
    TIM_ITConfig(IR_TIMER, TIM_IT_CC1 | TIM_IT_Update, ENABLE);
    TIM_Cmd(IR_TIMER, ENABLE);
}
/* 中断服务函数 */
void TIM3_IRQHandler(void) 
	{
//		u16 num;
    if(TIM_GetITStatus(IR_TIMER, TIM_IT_Update) != RESET) 
    {
        TIM_ClearITPendingBit(IR_TIMER, TIM_IT_Update);
			  TIM_SetCounter(IR_TIMER,0);
    }

    
    if(TIM_GetITStatus(IR_TIMER, TIM_IT_CC1) != RESET) 
    {
        if(enter_flag_verify)
        {
        
        if(~GPIO_ReadInputDataBit(IR_GPIO_PORT, IR_GPIO_PIN)) 
			  {
					    IR_Buffer_Row[Buffer_index]=TIM_GetCapture1(IR_TIMER);
					    Buffer_index++;
					    if(Buffer_index==99)
							{
								Buffer_index=0;
							  enter_flag_verify=0;
							}
        }
							TIM_SetCounter(IR_TIMER,0);
        }
        else
				{		
            enter_flag_verify=1;
            TIM_SetCounter(IR_TIMER,0);				
				}
        TIM_ClearITPendingBit(IR_TIMER, TIM_IT_CC1);
    }
	  }
u8  Lead_cheak(u16* rowdata)
{
  if(rowdata[0]>IR_Lead_MIN&&rowdata[0]<IR_Lead_MAX)
  {
    frame_num=1;
    return 1;
  }
  else
      return 0;
	
}
u8 Separation_cheak(u16* rowdata)
{
  if(rowdata[49]>SEPARATION_MIN&&rowdata[49]<SEPARATION_MAX)
  {
    frame_num=2;
    return 1;
  }
  else
    return 0;
	
}

u8 decode_A(u16* rowdata)
{	  
    u8 i=0;
	  if(frame_num==1)//解码第一帧
		{
       for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+1]);
        if(rowdata[i+1]>IR_DATA_1_MIN&&rowdata[i+1]<IR_DATA_1_MAX)
          ir_data[0]|=(0x80>>i);
        else if (rowdata[i+1]<IR_DATA_0_MAX&&rowdata[i+1]>IR_DATA_0_MIN)
          ir_data[0]&=~(0x80>>i);
      }
		}else if(frame_num==2)//解码第二帧
		{
			 for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+51]);
        if(rowdata[i+51]>IR_DATA_1_MIN&&rowdata[i+51]<IR_DATA_1_MAX)
          ir_data[0]|=(0x80>>i);
        else if (rowdata[i+51]<IR_DATA_0_MAX&&rowdata[i+51]>IR_DATA_0_MIN)
          ir_data[0]&=~(0x80>>i);
      }
		}
    return ir_data[0];   
}
u8 decode_NA(u16* rowdata)
{
    u8 i=0;
	  if(frame_num==1)
		{
    for(i=0;i<8;i++)
    {
		//printf("%d\n",rowdata[i+9]);
        if(rowdata[i+9]>IR_DATA_1_MIN&&rowdata[i+9]<IR_DATA_1_MAX)
          ir_data[1]|=(0x80>>i);
          else if (rowdata[i+9]<IR_DATA_0_MAX&&rowdata[i+9]>IR_DATA_0_MIN)
          ir_data[1]&=~(0x80>>i);
    }
	  }
		else if(frame_num==2)
		{
			 for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+59]);
        if(rowdata[i+59]>IR_DATA_1_MIN&&rowdata[i+59]<IR_DATA_1_MAX)
          ir_data[1]|=(0x80>>i);
        else if (rowdata[i+59]<IR_DATA_0_MAX&&rowdata[i+59]>IR_DATA_0_MIN)
          ir_data[1]&=~(0x80>>i);
      }
		}
    return ir_data[1];   
}
u8 decode_B(u16* rowdata)
{
    u8 i=0;
	  if(frame_num==1)
		{
    for(i=0;i<8;i++)
    {
		//printf("%d\n",rowdata[i+17]);
        if(rowdata[i+17]>IR_DATA_1_MIN&&rowdata[i+17]<IR_DATA_1_MAX)
          ir_data[2]|=(0x80>>i);
          else if (rowdata[i+17]<IR_DATA_0_MAX&&rowdata[i+17]>IR_DATA_0_MIN)
          ir_data[2]&=~(0x80>>i);
    }
	  }else if(frame_num==2)
		{
		for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+67]);
        if(rowdata[i+67]>IR_DATA_1_MIN&&rowdata[i+67]<IR_DATA_1_MAX)
          ir_data[2]|=(0x80>>i);
        else if (rowdata[i+67]<IR_DATA_0_MAX&&rowdata[i+67]>IR_DATA_0_MIN)
          ir_data[2]&=~(0x80>>i);
      }
		}
		
    return ir_data[2];   
}
u8 decode_NB(u16* rowdata)
{
	  
    u8 i=0;
	if(frame_num==1)
	{
    for(i=0;i<8;i++)
    {
		//printf("%d\n",rowdata[i+25]);
        if(rowdata[i+25]>IR_DATA_1_MIN&&rowdata[i+25]<IR_DATA_1_MAX)
          ir_data[3]|=(0x80>>i);
          else if (rowdata[i+25]<IR_DATA_0_MAX&&rowdata[i+25]>IR_DATA_0_MIN)
          ir_data[3]&=~(0x80>>i);
    }
	}else if(frame_num==2)
	{
		for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+75]);
        if(rowdata[i+75]>IR_DATA_1_MIN&&rowdata[i+75]<IR_DATA_1_MAX)
          ir_data[3]|=(0x80>>i);
        else if (rowdata[i+75]<IR_DATA_0_MAX&&rowdata[i+75]>IR_DATA_0_MIN)
          ir_data[3]&=~(0x80>>i);
      }
	}
    return ir_data[3];   
}
u8 decode_C(u16* rowdata)
{
	  
    u8 i=0;
	  if(frame_num==1)
		{
    for(i=0;i<8;i++)
    {
		//printf("%d\n",rowdata[i+33]);
        if(rowdata[i+33]>IR_DATA_1_MIN&&rowdata[i+33]<IR_DATA_1_MAX)
          ir_data[4]|=(0x80>>i);
          else if (rowdata[i+33]<IR_DATA_0_MAX&&rowdata[i+33]>IR_DATA_0_MIN)
          ir_data[4]&=~(0x80>>i);
    }
	  }else if(frame_num==2)
		{
	  for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+83]);
        if(rowdata[i+83]>IR_DATA_1_MIN&&rowdata[i+83]<IR_DATA_1_MAX)
          ir_data[4]|=(0x80>>i);
        else if (rowdata[i+83]<IR_DATA_0_MAX&&rowdata[i+83]>IR_DATA_0_MIN)
          ir_data[4]&=~(0x80>>i);
      }
		}
    return ir_data[4];   
}
u8 decode_NC(u16* rowdata)
{
    u8 i=0;
	if(frame_num==1)
	{
    for(i=0;i<8;i++)
    {
		//printf("%d\n",rowdata[i+41]);
        if(rowdata[i+41]>IR_DATA_1_MIN&&rowdata[i+41]<IR_DATA_1_MAX)
          ir_data[5]|=(0x80>>i);
          else if (rowdata[i+41]<IR_DATA_0_MAX&&rowdata[i+41]>IR_DATA_0_MIN)
          ir_data[5]&=~(0x80>>i);
    }
	}else if(frame_num==2)
	{
		for(i=0;i<8;i++)
      {
//        printf("%d\n",rowdata[i+91]);
        if(rowdata[i+91]>IR_DATA_1_MIN&&rowdata[i+91]<IR_DATA_1_MAX)
          ir_data[5]|=(0x80>>i);
        else if (rowdata[i+91]<IR_DATA_0_MAX&&rowdata[i+91]>IR_DATA_0_MIN)
          ir_data[5]&=~(0x80>>i);
      }
	}
    return ir_data[5];   
}
/*
  功能:对接收到的原始数据进行解码
  输入:IR_Buffer_Row 原始数据数组首地址
  输出:ir_data[x],x=0->A,x=1->NA,x=2->B,x=3->NB,x=4->C,x=5->NC,
*/
u8 Decode_IRDta(void)
{
	if(Lead_cheak(IR_Buffer_Row))//对第一帧Lead检查
	{

		if((decode_A(IR_Buffer_Row)==(u8)~decode_NA(IR_Buffer_Row))&&
			 (decode_B(IR_Buffer_Row)==(u8)~decode_NB(IR_Buffer_Row))&&
		   (decode_C(IR_Buffer_Row)==(u8)~decode_NC(IR_Buffer_Row)))//对第一帧数据进行解码并反码校验
		{
			  frame_num=0;
			  return 1;
		}
		else{ 
			    if(Separation_cheak(IR_Buffer_Row))//第一帧检查失败,进行间隔码检查
		      {
			       if((decode_A(IR_Buffer_Row)==(u8)~decode_NA(IR_Buffer_Row))&&
			          (decode_B(IR_Buffer_Row)==(u8)~decode_NB(IR_Buffer_Row))&&
		            (decode_C(IR_Buffer_Row)==(u8)~decode_NC(IR_Buffer_Row)))//间隔码检查通过对第二帧数据进行解码并反码晓校验
			           //第二帧解码前没有对第二帧的Lead码进行验证,但我觉得没有必要,那我就不验证哒
			          {
									frame_num=0;
			            return 2;
			          }else
			            {//第二帧反码也校验识别,没得玩了
										frame_num=0;
			              return 3;
			            }
		      }
		      else
			    return 4;//间隔码检测失败
				}
	}
	else
	{
		frame_num=0;
		return 5;
	}
}

5 加湿器模块

加湿器模块通过开关按键实现控制,按一下切换模式,模式有 “关”、“低”、“高” 三种模式
通过延时置位复位IO达到模拟按键的作用。
代码实现如下

#include "Humidifier.h"
#include "stdio.h"
/*
  功能:加湿器使用IO初始化
  参数:无
*/
void RH_GPIO_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}
/*
  功能:加湿器按键模拟
  参数:num
  说明:num表示按几次按键
*/
void PressKey(u8 num)
{
	while(num--)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_5);
		delay_ms(500);
		GPIO_ResetBits(GPIOB, GPIO_Pin_5);
		delay_ms(500);
	}
}
/*
  功能:加湿器模式控制
  参数:RH_State sta 模式的枚举变量
  说明:通过与最后一次模式的比较,判断需要按几次按键,以完成模式控制
*/
void RH_Control(RH_State sta)
{
	RH_State laststa;
   if(sta!=laststa)
	{
		switch(laststa)
		{
			case State_Low: //上次是低
			{
				if(sta==State_OFF)
				PressKey(2);
				else if(sta==State_High)
				PressKey(1);
			}
			case State_OFF:
			{
				if(sta==State_Low)
				PressKey(1);
				else if(sta==State_High)
				PressKey(2);
			}
			case State_High :
			{
				if(sta==State_OFF)
				PressKey(1);
				else if(sta==State_Low)
				PressKey(2);
			}
			default: printf("Error!\n");
		}
	}
	laststa=sta;
}

6.串口初始化

头文件代码,主要完成串口的存储缓冲区定义

#ifndef _usart_H
#define _usart_H
#include "stm32f10x.h"                  // Device header

#if SYSTEM_SUPPPORT_RTOS
#include "FreeRTOS.h"
#endif

#define USART1_REC_LEN		200  	//设置UATR1的最大数据长度 200
#define USART3_REC_LEN		100  	//设置UATR1的最大数据长度 7

extern u8  USART1_RX_BUF[USART1_REC_LEN]; //外部变量为USART_REC_LEN长度的一个数组 
extern u16 USART1_RX_STA;         		//定义16为数据接收状态信息变量

extern u8  USART3_RX_BUF[USART3_REC_LEN]; //外部变量为USART_REC_LEN长度的一个数组 
extern u16 USART3_RX_STA;         		//定义16为数据接收状态信息变量

void USART1_Init(u32 bound);
void USART3_Init(u32 bound);
void send_float_via_uart(USART_TypeDef* USARTx, float data);

#endif

使用USART3作为与ESP01S 通信接口,USART1 作为调试使用

#include "usart.h"
#include "stdio.h"
#include "delay.h"
int fputc(int ch,FILE *p)  //该函数用于在使用printf时发送字符到串口
{
	USART_SendData(USART1,(u8)ch);	
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	return ch;
}
   	
u8 USART1_RX_BUF[USART1_REC_LEN];     //自定义了一个数组,可能用于接收缓冲器
//其他位可能用于表示错误状态或额外的控制信息̬
//bit15接收完成标志位,当接收到完整的数据包(如以特定的结束符结尾)时,该位被置1
//bit14表示是否接收到特定的字符(如回车符\r,即0x0D)
//bit13~0存储接收到的数据长度
u16 USART1_RX_STA=0;       //存储串口接收的状态信息

u8 USART3_RX_BUF[USART3_REC_LEN];     //自定义了一个数组,可能用于接收缓冲器
//其他位可能用于表示错误状态或额外的控制信息̬
//bit15接收完成标志位,当接收到完整的数据包(如以特定的结束符结尾)时,该位被置1
//bit14表示是否接收到特定的字符(如回车符\r,即0x0D)
//bit13~0存储接收到的数据长度
u16 USART3_RX_STA=0;       //存储串口接收的状态信息


/*******************************************************************************
* 函数名   : USART1_Init
* 功能		 : USART1初始化
* 参数     : bound:串口波特率
* 返回值   : 无
*******************************************************************************/ 
void USART1_Init(u32 bound)
{
   //GPIO的IO口、串口及中断管理结构变量定义
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	//开GPIO和串口时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE);
 
	// 重映射USART1到PB6/PB7
  GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	
	/*  GPIO参数设置 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//TX			   //UART1的TX口设置为PB6
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	    //推挽输出
	GPIO_Init(GPIOB,&GPIO_InitStructure);  /* 应用GPIO的IO参数 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;//RX			 //UART1的RX口设置为PB7
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		  //设置浮空输入
	GPIO_Init(GPIOB,&GPIO_InitStructure); /* 初始化UART1的接收GPIO */
	

	//USART1 结构变量参数设置
	USART_InitStructure.USART_BaudRate = bound;//串口波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据帧的位数,此处8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位数为1位
	USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶效验,此处关
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制选项,既通过再加一条RTS时钟线来防止数据丢失
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//串口模式,输入输出同时使用
	USART_Init(USART1, &USART_InitStructure); //串口参数初始化使用
	
	USART_Cmd(USART1, ENABLE);  //使能UART1 
	
	USART_ClearFlag(USART1, USART_FLAG_TC);//清除UART标准位,在这里是清除UART1的发送完成标志位
		
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//USART_IT_RXNE接收寄存器非空中断,串口中断配置,在这里使能了这个中断

	//Usart1 NVIC 中断管理
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//选择中断函数
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级,数字越低优先级越高
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//等待优先级,在两个中断具备同等的抢占优先级时,等待优先级数字高的优先级越高
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ中断使能
	NVIC_Init(&NVIC_InitStructure);	//初始化优先级
}

/*******************************************************************************
* 函数名      : USART1_IRQHandler
* 功能		    : USART1接收寄存器满了发生中断
* 参数        : 无
* 返回值      : 无
* 描述        : 接收数据,存在USART1_RX_BUF[]数组中,但是接收到回车会进入判断,若只有回车会复位USART1_RX_BUF[]数组从0开始存储,若是回车+换行则不能再接收数据
*******************************************************************************/ 
void USART1_IRQHandler(void)                	
{
	u8 r;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //如果串口接收寄存器非空标准位为1,该中断以开,即已经接收到了数据
	{
		r =USART_ReceiveData(USART1);//(USART1->DR);	//将接收到的数据放入r中
		if((USART1_RX_STA&0x8000)==0)//还没检查到了换行?
		{
			if(USART1_RX_STA&0x4000)//是否检查到回车
			{
				if(r!=0x0a)USART1_RX_STA=0;//若接收到的数据不为换行,则初始化串口接收状态变量,重新接收,直到接收到,0x0a是换行符
				else USART1_RX_STA|=0x8000;	//置串口接收状态变量最高位为1,表接收完成 
			}
			//将USART1_RX_STA|=0x8000;表示此次数据接收任务结束,同时设置此标志位在下次数据输入时使数据进入缓存
			else //未接收到了,回车0X0D
			{	
				if(r==0x0d)USART1_RX_STA|=0x4000;//检查数据是否回车,将第14位置1
				else
				{
					USART1_RX_BUF[USART1_RX_STA&0X3FFF]=r;//将数据放在缓存中,0X3FFF是将0到13位置1,实际按STA来表示数据长度
					USART1_RX_STA++;
					if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//若数据长度超过USART1_REC_LEN,重新开始计数数据长度,覆盖原有数据	  
				}		 
			}
		}   		 
	} 
}

//int fputc(int ch, FILE *f)
//{
//	USART_TypeDef* USARTx =USART1;
//	while((USARTx->SR&(1<<7))==0);//"1<<7"表示01000000,与二进制01000000等价,SR是发送成功标志为,成功为1,不成功就等待
//	USARTx->DR = ch;
//	return ch;
//}

//uint8_t* GetBuffers(void)
//{
//	return receiveBuffer;
//}

//void USART1_IRQHandler(void)  
//{  
//    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  
//    {  
//        // 读取接收到的数据  
//        uint8_t receivedData = USART_ReceiveData(USART1);  
//          
//        // 将数据存储在缓冲区中  
//        USART1_RX_BUF[USART1_RX_STA++] = receivedData;  
//          
//        // 如果接收到换行符(\n),则处理缓冲区中的数据  
//        if (receivedData == '\n')  
//        {  
//            USART1_RX_STA = 0; // 重置缓冲区索引  
//            // 这里可以添加处理接收到的数据的逻辑  
//            //GPIO_ToggleBits(LED_PORT, LED_PIN); // 闪烁LED  
//        }  
//    }  
//}
/*******************************************************************************
* 函数名   : USART3_Init
* 功能		 : USART3初始化
* 参数     : bound:串口波特率
* 返回值   : 无
*******************************************************************************/ 
void USART3_Init(u32 bound)
{
   //GPIO的IO口、串口及中断管理结构变量定义
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	//开GPIO和串口时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	// 重映射USART1到PB6/PB7
    //GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	
	/*  GPIO参数设置 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//UART3的TX口设置为PB10
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	    //推挽输出
	GPIO_Init(GPIOB,&GPIO_InitStructure);  /* 应用GPIO的IO参数 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;//RX			 //UART3的RX口设置为PB11
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		  //设置浮空输入
	GPIO_Init(GPIOB,&GPIO_InitStructure); /* 初始化UART3的接收GPIO */
	

	//USART1 结构变量参数设置
	USART_InitStructure.USART_BaudRate = bound;//串口波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据帧的位数,此处8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位数为1位
	USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶效验,此处关
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制选项,既通过再加一条RTS时钟线来防止数据丢失
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//串口模式,输入输出同时使用
	USART_Init(USART3, &USART_InitStructure); //串口参数初始化使用
	
	USART_Cmd(USART3, ENABLE);  //使能UART1 
	
	USART_ClearFlag(USART3, USART_FLAG_TC);//清除UART标准位,在这里是清除UART1的发送完成标志位
		
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//USART_IT_RXNE接收寄存器非空中断,串口中断配置,在这里使能了这个中断

	//Usart1 NVIC 中断管理
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;//选择中断函数
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级,数字越低优先级越高
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//等待优先级,在两个中断具备同等的抢占优先级时,等待优先级数字高的优先级越高
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ中断使能
	NVIC_Init(&NVIC_InitStructure);	//初始化优先级
}
//发送float4字节
void send_float_via_uart(USART_TypeDef* USARTx, float data) 
{
    uint8_t *bytePtr = (uint8_t*)&data;
    for (int i = 0; i < 4; i++) {
			  delay_ms(100);
        USART_SendData(USART3,bytePtr[i]); // 按内存顺序发送字节
    }
}
void USART3_IRQHandler(void)  
{  
		static u8 FirstBytecheak;
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)  
    {  
        // 读取接收到的数据  
        uint8_t receivedData = USART_ReceiveData(USART3);  
        if(receivedData==0x47)
           FirstBytecheak=1;
        if(FirstBytecheak==1)				
          USART3_RX_BUF[USART3_RX_STA++] = receivedData; // 将数据存储在缓冲区中   
        if (USART3_RX_STA == 8)  
        {  
            USART3_RX_STA = 0; // 重置缓冲区索引  
			FirstBytecheak=0 ;
            // 这里可以添加处理接收到的数据的逻辑  
            //GPIO_ToggleBits(LED_PORT, LED_PIN); // 闪烁LED  
        }  
    }  
}

项目软硬件源文件:
通过网盘分享的文件:基于STM32(Freertos)&ESP01S的红外空调网络控制器
链接: https://pan.baidu.com/s/1v1wjqlfK64kvp4HNg9b2ew 提取码: vzx5
本文及项目文件仅用于学习交流,严禁用于商业用途,转载引用请注明出处。

posted @ 2025-04-05 21:22  SunFarmer  阅读(149)  评论(0)    收藏  举报