首页 > 其他分享 >STM32F103+FreeRTOS的使用ESP8266与手机APP实现TCP连接通信控制

STM32F103+FreeRTOS的使用ESP8266与手机APP实现TCP连接通信控制

时间:2024-07-31 13:23:57浏览次数:14  
标签:STM32F103 ESP8266 FreeRTOS void 0x00 uint8 OLED GPIO 0x20

前言

本人初学FreeRTOS,来自不知名普通院校,大二物联网专业,简单看完百问网韦东山老师FreeRTOS就想随便找个小项目试试看,手头里没什么元器件,只有一块ESP8266wifi模块以及温湿度模块显示屏模块,所以用到的模块不多,这俩个模块可能不太适用于FreeRTOS,但主要目的想着以最少的资源练练手,文中有缺陷或需补全的地方欢迎指导,大佬误喷。

需要主要的细节有:ESP8266模块与手机APP通信时候必须处于一个局域网,在手机APP打开之前需确保连上与ESP8266模块相同连接一个wifi,否则将导致连接不上。系统中断优先级需设计为分组四,在编写代码时触及的中断设置抢占优先级的时候必须要设置≥5,因为FreeRTOS中系统能控制的优先级为(5-15)。
其中代码有些许初始化部分是从江科大及网上搜寻而来,核心部分还是自己手敲的。

完整工程代码及手机app软件

百度网盘链接:百度网盘 请输入提取码

提取码:IOT1

一、stm32f103vet6移植使用FreeRTOS

百问网网址为:百问网嵌入式专家-韦东山嵌入式专注于嵌入式课程及硬件研发 

移植的细节不多介绍,在百问网里边包含有FreeRTOS移植模板教程及开发手册等。

二、LED初始化

LED1 ------->PC5

LED2 ------->PB2

 LED.c

#include "led.h"

/*******************************************************************************
* 函 数 名         : LED_Init
* 函数功能		   : LED初始化函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量
	
	RCC_APB2PeriphClockCmd(LED1_PORT_RCC|LED2_PORT_RCC,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=LED1_PIN | LED2_PIN ;  //选择你要设置的IO口
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;	 //设置推挽输出模式
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	  //设置传输速率
	GPIO_Init(LED1_PORT,&GPIO_InitStructure); 	   /* 初始化GPIO */
	GPIO_Init(LED2_PORT,&GPIO_InitStructure); 	   /* 初始化GPIO */	
	GPIO_SetBits(LED1_PORT,LED1_PIN);   //将LED端口拉高,熄灭所有LED
	GPIO_SetBits(LED2_PORT,LED2_PIN);  	
//	GPIO_ResetBits(LED1_PORT,LED1_PIN); 
//	GPIO_ResetBits(LED2_PORT,LED2_PIN); 	
}



 LED.h

#ifndef _led_H
#define _led_H

#include "system.h"

/*  LED时钟端口、引脚定义 */
#define LED1_PORT 			GPIOC   
#define LED1_PIN 				GPIO_Pin_5
#define LED1_PORT_RCC		RCC_APB2Periph_GPIOC

#define LED2_PORT 			GPIOB   
#define LED2_PIN 				GPIO_Pin_2
#define LED2_PORT_RCC		RCC_APB2Periph_GPIOB


#define LED1 PCout(5)  	
#define LED2 PBout(2)  	


void LED_Init(void);


#endif

 三、USART1及USART3初始化

USART1---CH340

PA9------>RX

PA10------>TX

USART3---ESP8266

PB10------>RX

PB11------>TX

 bsp_usart.c

#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "semphr.h"	
#include "task.h"
#include "queue.h"
#include "led.h"
#include "OLED.h"

extern SemaphoreHandle_t  uartSemaphore;
extern QueueHandle_t  uartQueue;
uint32_t rx_cnt=0;
xUSATR_TypeDef  xUSART;         // 声明为全局变量,方便记录信息、状态


/******************************************************************************
 * 函  数: vUSART1_Init
 * 功  能: 初始化USART1的GPIO、通信参数配置、中断优先级
 *          (8位数据、无校验、1个停止位)
 * 参  数: uint32_t baudrate  通信波特率
 * 返回值: 无
 ******************************************************************************/
void USART1_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 时钟使能
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;                           // 使能USART1时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;                             // 使能GPIOA时钟

    // GPIO_TX引脚配置
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;                // TX引脚工作模式:复用推挽
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // GPIO_RX引脚配置
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;                  // RX引脚工作模式:上拉输入; 如果使用浮空输入,引脚空置时可能产生误输入; 当电路上为一主多从电路时,可以使用复用开漏模式
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 中断配置
    NVIC_InitStructure .NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 6 ;     // 抢占优先级
    NVIC_InitStructure .NVIC_IRQChannelSubPriority = 0;             // 子优先级
    NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE;                // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);

    //USART 初始化设置
    USART_DeInit(USART1);
    USART_InitStructure.USART_BaudRate   = baudrate;                // 串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;     // 字长为8位数据格式
    USART_InitStructure.USART_StopBits   = USART_StopBits_1;        // 一个停止位
    USART_InitStructure.USART_Parity     = USART_Parity_No;         // 无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能收、发模式
    USART_Init(USART1, &USART_InitStructure);                       // 初始化串口

    USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                  // 使能接受中断
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);                  // 使能空闲中断

    USART_Cmd(USART1, ENABLE);                                      // 使能串口, 开始工作

    USART1->SR = ~(0x00F0);                                         // 清理中断

    xUSART.USART1InitFlag = 1;                                      // 标记初始化标志
    xUSART.USART1ReceivedNum = 0;                                   // 接收字节数清零

    printf("USART1初始化配置成功\r");
}

/******************************************************************************
 * 函  数: USART1_IRQHandler
 * 功  能: USART1的接收中断、空闲中断、发送中断
 * 参  数: 无
 * 返回值: 无
 *
******************************************************************************/
static uint8_t U1TxBuffer[256] ;    // 用于中断发送:环形缓冲区,256个字节
static uint8_t U1TxCounter = 0 ;    // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U1TxCount   = 0 ;    // 用于中断发送:标记将要发送的字节数(环形)

void USART1_IRQHandler(void)
{
    static uint16_t cnt = 0;                                         // 接收字节数累计:每一帧数据已接收到的字节数
    static uint8_t  RxTemp[U1_RX_BUF_SIZE];                          // 接收数据缓存数组:每新接收1个字节,先顺序存放到这里,当一帧接收完(发生空闲中断), 再转存到全局变量:xUSART.USARTxReceivedBuffer[xx]中;

    // 接收中断
    if (USART1->SR & (1 << 5))                                       // 检查RXNE(读数据寄存器非空标志位); RXNE中断清理方法:读DR时自动清理;
    {
        if ((cnt >= U1_RX_BUF_SIZE))//||(xUSART.USART1ReceivedFlag==1// 判断1: 当前帧已接收到的数据量,已满(缓存区), 为避免溢出,本包后面接收到的数据直接舍弃.
        {
            // 判断2: 如果之前接收好的数据包还没处理,就放弃新数据,即,新数据帧不能覆盖旧数据帧,直至旧数据帧被处理.缺点:数据传输过快于处理速度时会掉包;好处:机制清晰,易于调试
            USART1->DR;                                              // 读取数据寄存器的数据,但不保存.主要作用:读DR时自动清理接收中断标志;
            return;
        }
        RxTemp[cnt++] = USART1->DR ;                                 // 把新收到的字节数据,顺序存放到RXTemp数组中;注意:读取DR时自动清零中断位;
    }

    // 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
    if (USART1->SR & (1 << 4))                                       // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR;  USART1 ->DR;
    {
        xUSART.USART1ReceivedNum  = 0;                               // 把接收到的数据字节数清0
        memcpy(xUSART.USART1ReceivedBuffer, RxTemp, U1_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
        xUSART.USART1ReceivedNum  = cnt;                             // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
        cnt = 0;                                                     // 接收字节数累计器,清零; 准备下一次的接收
        memset(RxTemp, 0, U1_RX_BUF_SIZE);                           // 接收数据缓存数组,清零; 准备下一次的接收
        USART1 ->SR;
        USART1 ->DR;                                 // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
    }

    // 发送中断
    if ((USART1->SR & 1 << 7) && (USART1->CR1 & 1 << 7))             // 检查TXE(发送数据寄存器空)、TXEIE(发送缓冲区空中断使能)
    {
        USART1->DR = U1TxBuffer[U1TxCounter++];                      // 读取数据寄存器值;注意:读取DR时自动清零中断位;
        if (U1TxCounter == U1TxCount)
            USART1->CR1 &= ~(1 << 7);                                // 已发送完成,关闭发送缓冲区空置中断 TXEIE
    }
}

/******************************************************************************
 * 函  数: vUSART1_GetBuffer
 * 功  能: 获取UART所接收到的数据
 * 参  数: uint8_t* buffer   数据存放缓存地址
 *          uint8_t* cnt      接收到的字节数
 * 返回值: 0_没有接收到新数据, 非0_所接收到新数据的字节数
 ******************************************************************************/
uint8_t USART1_GetBuffer(uint8_t *buffer, uint8_t *cnt)
{
    if (xUSART.USART1ReceivedNum > 0)                                           // 判断是否有新数据
    {
        memcpy(buffer, xUSART.USART1ReceivedBuffer, xUSART.USART1ReceivedNum);  // 把新数据复制到指定位置
        *cnt = xUSART.USART1ReceivedNum;                                        // 把新数据的字节数,存放指定变量
        xUSART.USART1ReceivedNum = 0;                                           // 接收标记置0
        return *cnt;                                                            // 返回所接收到新数据的字节数
    }
    return 0;                                                                   // 返回0, 表示没有接收到新数据
}

/******************************************************************************
 * 函  数: vUSART1_SendData
 * 功  能: UART通过中断发送数据,适合各种数据类型
 *         【适合场景】本函数可发送各种数据,而不限于字符串,如int,char
 *         【不 适 合】注意环形缓冲区容量256字节,如果发送频率太高,注意波特率
 * 参  数: uint8_t* buffer   需发送数据的首地址
 *          uint8_t  cnt      发送的字节数 ,限于中断发送的缓存区大小,不能大于256个字节
 * 返回值:
 ******************************************************************************/
void USART1_SendData(uint8_t *buf, uint8_t cnt)
{
		uint8_t i; 
    for (i = 0; i < cnt; i++)
        U1TxBuffer[U1TxCount++] = buf[i];

    if ((USART1->CR1 & 1 << 7) == 0)       // 检查发送缓冲区空置中断(TXEIE)是否已打开
        USART1->CR1 |= 1 << 7;
}

/******************************************************************************
 * 函  数: vUSART1_SendString
 * 功  能: UART通过中断发送输出字符串,无需输入数据长度
 *         【适合场景】字符串,长度<=256字节
 *         【不 适 合】int,float等数据类型
 * 参  数: char* stringTemp   需发送数据的缓存首地址
 * 返回值: 元
 ******************************************************************************/
void USART1_SendString(char *stringTemp)
{
    u16 num = 0;                                 // 字符串长度
    char *t = stringTemp ;                       // 用于配合计算发送的数量
    while (*t++ != 0)  num++;                    // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
    USART1_SendData((u8 *)stringTemp, num);      // 注意调用函数所需要的真实数据长度; 如果目标需要以0作结尾判断,需num+1:字符串以0结尾,即多发一个:0
}

/******************************************************************************
 * 函  数: vUSART1_SendStringForDMA
 * 功  能: UART通过DMA发送数据,省了占用中断的时间
 *         【适合场景】字符串,字节数非常多,
 *         【不 适 合】1:只适合发送字符串,不适合发送可能含0的数值类数据; 2-时间间隔要足够
 * 参  数: char strintTemp  要发送的字符串首地址
 * 返回值: 无
 ******************************************************************************/
void USART1_SendStringForDMA(char *stringTemp)
{
    static u8 Flag_DmaTxInit = 0;                // 用于标记是否已配置DMA发送
    u32   num = 0;                               // 发送的数量,注意发送的单位不是必须8位的
    char *t = stringTemp ;                       // 用于配合计算发送的数量

    while (*t++ != 0)  num++;                    // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位

    while (DMA1_Channel4->CNDTR > 0);            // 重要:如果DMA还在进行上次发送,就等待; 得进完成中断清标志,F4不用这么麻烦,发送完后EN自动清零
    if (Flag_DmaTxInit == 0)                     // 是否已进行过USAART_TX的DMA传输配置
    {
        Flag_DmaTxInit  = 1;                     // 设置标记,下次调用本函数就不再进行配置了
        USART1 ->CR3   |= 1 << 7;                // 使能DMA发送
        RCC->AHBENR    |= 1 << 0;                // 开启DMA1时钟  [0]DMA1   [1]DMA2

        DMA1_Channel4->CCR   = 0;                // 失能, 清0整个寄存器, DMA必须失能才能配置
        DMA1_Channel4->CNDTR = num;              // 传输数据量
        DMA1_Channel4->CMAR  = (u32)stringTemp;  // 存储器地址
        DMA1_Channel4->CPAR  = (u32)&USART1->DR; // 外设地址

        DMA1_Channel4->CCR |= 1 << 4;            // 数据传输方向   0:从外设读   1:从存储器读
        DMA1_Channel4->CCR |= 0 << 5;            // 循环模式       0:不循环     1:循环
        DMA1_Channel4->CCR |= 0 << 6;            // 外设地址非增量模式
        DMA1_Channel4->CCR |= 1 << 7;            // 存储器增量模式
        DMA1_Channel4->CCR |= 0 << 8;            // 外设数据宽度为8位
        DMA1_Channel4->CCR |= 0 << 10;           // 存储器数据宽度8位
        DMA1_Channel4->CCR |= 0 << 12;           // 中等优先级
        DMA1_Channel4->CCR |= 0 << 14;           // 非存储器到存储器模式
    }
    DMA1_Channel4->CCR  &= ~((u32)(1 << 0));     // 失能,DMA必须失能才能配置
    DMA1_Channel4->CNDTR = num;                  // 传输数据量
    DMA1_Channel4->CMAR  = (u32)stringTemp;      // 存储器地址
    DMA1_Channel4->CCR  |= 1 << 0;               // 开启DMA传输
}




//   USART-3   //
/
/******************************************************************************
 * 函  数: vUSART3_Init
 * 功  能: 初始化USART的GPIO、通信参数配置、中断优先级
 *          (8位数据、无校验、1个停止位)
 * 参  数: uint32_t baudrate  通信波特率
 * 返回值: 无
 ******************************************************************************/
void USART3_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 时钟使能
    RCC->APB1ENR |= RCC_APB1ENR_USART3EN;                           // 使能USART3时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;                             // 使能GPIOB时钟

    // GPIO_TX引脚配置
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;                // TX引脚工作模式:复用推挽
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // GPIO_RX引脚配置
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;                  // RX引脚工作模式:上拉输入; 如果使用浮空输入,引脚空置时可能产生误输入; 当电路上为一主多从电路时,可以使用复用开漏模式
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 中断配置
    NVIC_InitStructure .NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 5;     // 抢占优先级
    NVIC_InitStructure .NVIC_IRQChannelSubPriority = 0;             // 子优先级
    NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE;                // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);

    //USART 初始化设置
    USART_DeInit(USART3);
    USART_InitStructure.USART_BaudRate   = baudrate;                // 串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;     // 字长为8位数据格式
    USART_InitStructure.USART_StopBits   = USART_StopBits_1;        // 一个停止位
    USART_InitStructure.USART_Parity     = USART_Parity_No;         // 无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能收、发模式
    USART_Init(USART3, &USART_InitStructure);                       // 初始化串口

    USART_ITConfig(USART3, USART_IT_TXE, DISABLE);
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);                  // 使能接受中断
    USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);                  // 使能空闲中断

    USART_Cmd(USART3, ENABLE);  
    
    USART3->SR = ~(0x00F0);                                         // 清理中断

    xUSART.USART3InitFlag = 1;                                      // 标记初始化标志
    xUSART.USART3ReceivedNum = 0;                                   // 接收字节数清零

    printf("\rUSART3初始化配置\r");
}

/******************************************************************************
 * 函  数: USART3_IRQHandler
 * 功  能: USART的接收中断、空闲中断、发送中断
 * 参  数: 无
 * 返回值: 无
 ******************************************************************************/
static uint8_t U3TxBuffer[256] ;    // 用于中断发送:环形缓冲区,256个字节
static uint8_t U3TxCounter = 0 ;    // 用于中断发送:标记已发送的字节数(环形)
static uint8_t U3TxCount   = 0 ;    // 用于中断发送:标记将要发送的字节数(环形)
static uint16_t cnt = 0;  
char  RxTemp[U3_RX_BUF_SIZE];  

void USART3_IRQHandler(void)
{
		BaseType_t xHigherPriorityTaskWoken;
		BaseType_t QueuePriorityTaskWoken;
		char *buffer;
    // 接收中断
    if (USART3->SR & (1 << 5))                                       // 检查RXNE(读数据寄存器非空标志位); RXNE中断清理方法:读DR时自动清理;
    {
        if ((cnt >= U3_RX_BUF_SIZE))//||xUSART.USART3ReceivedFlag==1 // 判断1: 当前帧已接收到的数据量,已满(缓存区), 为避免溢出,本包后面接收到的数据直接舍弃.
        {
            // 判断2: 如果之前接收好的数据包还没处理,就放弃新数据,即,新数据帧不能覆盖旧数据帧,直至旧数据帧被处理.缺点:数据传输过快于处理速度时会掉包;好处:机制清晰,易于调试
            USART3->DR;                                              // 读取数据寄存器的数据,但不保存.主要作用:读DR时自动清理接收中断标志;
            return;
        }
        RxTemp[cnt++] = USART3->DR ;                                 // 把新收到的字节数据,顺序存放到RXTemp数组中;注意:读取DR时自动清零中断位;
    }

    // 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
    if (USART3->SR & (1 << 4))                                       // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR;  USART1 ->DR;
    {
				if(uartSemaphore!=NULL)
				{					
					//释放二值信号量
					xSemaphoreGiveFromISR(uartSemaphore,&xHigherPriorityTaskWoken);	//释放二值信号量
				}
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换			  
        xUSART.USART3ReceivedNum  = 0;                               // 把接收到的数据字节数清0
        memcpy(xUSART.USART3ReceivedBuffer, RxTemp, U3_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
				buffer = xUSART.USART3ReceivedBuffer;
				xQueueSendToBackFromISR( uartQueue, &buffer,&QueuePriorityTaskWoken); //将数据写入队列
				portYIELD_FROM_ISR(QueuePriorityTaskWoken);	//上下文切换			
				xUSART.USART3ReceivedNum  = cnt;                             // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
				cnt = 0;                                                     // 接收字节数累计器,清零; 准备下一次的接收
        memset(RxTemp, 0, U3_RX_BUF_SIZE);                           // 接收数据缓存数组,清零; 准备下一次的接收
        USART3 ->SR;
        USART3 ->DR;                                                 // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
    }

    // 发送中断
    if ((USART3->SR & 1 << 7) && (USART3->CR1 & 1 << 7))             // 检查TXE(发送数据寄存器空)、TXEIE(发送缓冲区空中断使能)
    {
        USART3->DR = U3TxBuffer[U3TxCounter++];                      // 读取数据寄存器值;注意:读取DR时自动清零中断位;
        if (U3TxCounter == U3TxCount)
            USART3->CR1 &= ~(1 << 7);                                // 已发送完成,关闭发送缓冲区空置中断 TXEIE
    }
}
/*
		功能:解析服务器下发数据,服务器不可使用中文下发数据,可使用数字及英文
*/
void Receive_Task(void *pvParameters)
{

	BaseType_t err = pdFALSE;
	BaseType_t xStatus;
	char *buffer;
	ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待初始化完成后唤醒任务
	debug_printf("Receive_Task!\n\n");	
	xSemaphoreTake(uartSemaphore,portMAX_DELAY);	//获取信号量
	xStatus = xQueueReceive( uartQueue, &buffer, portMAX_DELAY );
	
	while(1)
	{
		err=xSemaphoreTake(uartSemaphore,portMAX_DELAY);	//获取空闲中断信号量
		if(err==pdTRUE)							//获取信号量成功
		{  
				debug_printf("获取信号量成功\r\n");
		}
		xStatus = xQueueReceive( uartQueue, &buffer, portMAX_DELAY ); /* 只需要写入4字节, 无需写入整个buffer */
		if(xStatus==pdTRUE && xUSART.USART3ReceivedNum > 0)
		{
				debug_printf("成功获取队列:%s\r\n",buffer);
				OLED_ShowString(1,1,(char *)buffer);  
		}
    if (strstr((char *)buffer, "LED20"))	LED2=0;//服务器下发控制解析指令
    if (strstr((char *)buffer, "LED21"))  LED2=1;	
		xUSART.USART3ReceivedNum = 0;                                          // 接收标记置0                                                        // 返回所接收到新数据的字节数
		memset(xUSART.USART3ReceivedBuffer,0,sizeof(xUSART.USART3ReceivedBuffer));		
	}
}



/******************************************************************************
 * 函  数: vUSART3_SendData
 * 功  能: UART通过中断发送数据,适合各种数据类型
 *         【适合场景】本函数可发送各种数据,而不限于字符串,如int,char
 *         【不 适 合】注意环形缓冲区容量256字节,如果发送频率太高,注意波特率
 * 参  数: uint8_t* buffer   需发送数据的首地址
 *          uint8_t  cnt      发送的字节数 ,限于中断发送的缓存区大小,不能大于256个字节
 * 返回值:
 ******************************************************************************/
void USART3_SendData(uint8_t *buf, uint8_t cnt)
{
		uint8_t i;
    for (i = 0; i < cnt; i++)
        U3TxBuffer[U3TxCount++] = buf[i];

    if ((USART3->CR1 & 1 << 7) == 0)       // 检查发送缓冲区空置中断(TXEIE)是否已打开
        USART3->CR1 |= 1 << 7;
}

/******************************************************************************
 * 函  数: vUSART3_SendString
 * 功  能: UART通过中断发送输出字符串,无需输入数据长度
 *         【适合场景】字符串,长度<=256字节
 *         【不 适 合】int,float等数据类型
 * 参  数: char* stringTemp   需发送数据的缓存首地址
 * 返回值: 元
 ******************************************************************************/
void USART3_SendString(char *stringTemp)
{
    u16 num = 0;                                 // 字符串长度
    char *t = stringTemp ;                       // 用于配合计算发送的数量
    while (*t++ != 0)  num++;                    // 计算要发送的数目,这步比较耗时,测试发现每多6个字节,增加1us,单位:8位
    USART3_SendData((u8 *)stringTemp, num);      // 注意调用函数所需要的真实数据长度; 如果目标需要以0作结尾判断,需num+1:字符串以0结尾,即多发一个:0
}




//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#pragma import(__use_no_semihosting)
struct __FILE
{
    int handle;
};                     // 标准库需要的支持函数
FILE __stdout;         // FILE 在stdio.h文件
void _sys_exit(int x)
{
    x = x;             // 定义_sys_exit()以避免使用半主机模式
}


int fputc(int ch, FILE *f)                   // 重定向fputc函数,使printf的输出,由fputc输出到UART,  这里使用串口1(USART1)
{
#if 1                                        // 方式1-使用常用的poll方式发送数据,比较容易理解,但等待耗时大  
    while ((USART1->SR & 0X40) == 0);        // 等待上一次串口数据发送完成
    USART1->DR = (u8) ch;                    // 写DR,串口1将发送数据
    return ch;
#else                                        // 方式2-使用queue+中断方式发送数据; 无需像方式1那样等待耗时,但要借助已写好的函数、环形缓冲
    uint8_t c[1] = {(uint8_t)ch};
    if (USARTx_DEBUG == USART1)    vUSART1_SendData(c, 1);
    if (USARTx_DEBUG == USART2)    vUSART2_SendData(c, 1);
    if (USARTx_DEBUG == USART3)    vUSART3_SendData(c, 1);
    if (USARTx_DEBUG == UART4)     vUART4_SendData(c, 1);
    if (USARTx_DEBUG == UART5)     vUART5_SendData(c, 1);
    return ch;
#endif
}

 bsp_usart.h

#ifndef __BSP__USART_H
#define __BSP__USART_H

#include <stm32f10x.h>  
#include <stdio.h>
#include "string.h"      

/*****************************************************************************
 ** 移植配置
****************************************************************************/
// 用哪个串口与上位机通信,可自行
#define USARTx_DEBUG            USART1              // 用于重定向printf, 使printf通过USARTx发送数据
// 数据接收缓冲区大小,可自行修改
#define U1_RX_BUF_SIZE            1024              // 配置每个USARTx接收缓冲区的大小(字节数),包括中断里的缓存大小,和xUSART结构体里的缓存区大小
#define U3_RX_BUF_SIZE            1024              // --- 当每帧接收到的数据字节数,超过此值时,超出部分,将在中断中直接弃舍,直到接收结束(发生空闲中断); 


/*
		为确保串口发送时不被其他任务抢占夺取串口资源,使用暂停任务调度或信号量俩种锁,这里使用暂停任务调度
*/
#define debug_printf(format, args...)       do{                           \
                         vTaskSuspendAll();                     \
                         printf(format, ##args); \
												 if(xTaskResumeAll()){taskYIELD(); }}while(0)

//#define debug_printf(format, args...)       do{                           \
//                         if(xSemaphoreTake(xMutex, 100) == pdFALSE)   \
//                         return;                     \
//                         printf(format, ##args); \
//                         xSemaphoreGive(xMutex); }while(0)

typedef struct 
{
    uint8_t   USART1InitFlag;                       // 初始化标记; 0=未初始化, 1=已初始化
    uint16_t  USART1ReceivedNum;                    // 接收到多少个字节数据; 当等于0时,表示没有接收到数据; 当大于0时,表示已收到一帧新数据
    uint8_t   USART1ReceivedBuffer[U1_RX_BUF_SIZE]; // 接收到数据的缓存
    
    
    uint8_t   USART3InitFlag;                       // 初始化标记; 0=未初始化, 1=已初始化
    uint16_t  USART3ReceivedNum;                    // 接收到多少个字节数据; 当等于0时,表示没有接收到数据; 当大于0时,表示已收到一帧新数据
    char   USART3ReceivedBuffer[U3_RX_BUF_SIZE]; // 接收到数据的缓存 
   
    
    uint16_t  testCNT;                              // 仅用于测试
    
}xUSATR_TypeDef;

extern xUSATR_TypeDef  xUSART;                      // 声明为全局变量,方便记录信息、状态
    

// USART1
void    USART1_Init (uint32_t baudrate);                      // 初始化串口的GPIO、通信参数配置、中断优先级; (波特率可设、8位数据、无校验、1个停止位)
uint8_t USART1_GetBuffer (uint8_t* buffer, uint8_t* cnt);     // 获取接收到的数据
void    USART1_SendData (uint8_t* buf, uint8_t cnt);          // 通过中断发送数据,适合各种数据
void    USART1_SendString (char* stringTemp);                 // 通过中断发送字符串,适合字符串,长度在256个长度内的
void    USART1_printfForDMA (char* stringTemp) ;              // 通过DMA发送数据,适合一次过发送数据量特别大的字符串,省了占用中断的时间
// USART3
void    USART3_Init (uint32_t baudrate);                      // 初始化串口的GPIO、通信参数配置、中断优先级; (波特率可设、8位数据、无校验、1个停止位)
uint8_t USART3_GetBuffer (uint8_t* buffer, uint8_t* cnt);     // 获取接收到的数据
void    USART3_SendData (uint8_t* buf, uint8_t cnt);          // 通过中断发送数据,适合各种数据
void    USART3_SendString (char* stringTemp);                 // 通过中断发送字符串,适合字符串,长度在256个长度内的
void Receive_Task(void *pvParameters);
#endif

四、四针0.96OLED屏幕(IIC通信)

引脚连接

PA15 -------> SCL
PD10 -------> SD

OLED.c

#include "OLED_Font.h"
#include "OLED.h"

/*引脚配置*/
#define OLED_W_SCL(x)		GPIO_WriteBit(GPIOA, GPIO_Pin_15, (BitAction)(x))
#define OLED_W_SDA(x)		GPIO_WriteBit(GPIOD, GPIO_Pin_10, (BitAction)(x))

/*引脚初始化*/
void OLED_I2C_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD ,ENABLE);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能端口复用时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//失能JTAG
	
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void)
{
	OLED_W_SDA(1);
	OLED_W_SCL(1);
	OLED_W_SDA(0);
	OLED_W_SCL(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		OLED_W_SDA(Byte & (0x80 >> i));
		OLED_W_SCL(1);
		OLED_W_SCL(0);
	}
	OLED_W_SCL(1);	//额外的一个时钟,不处理应答信号
	OLED_W_SCL(0);
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x00);		//写命令
	OLED_I2C_SendByte(Command); 
	OLED_I2C_Stop();
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x40);		//写数据
	OLED_I2C_SendByte(Data);
	OLED_I2C_Stop();
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置高4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置低4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	vTaskSuspendAll();//禁止任务调度,确保IIC时序完整性
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
	if(xTaskResumeAll())//连接上下文
	 {
				taskYIELD();
	 }	
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	vTaskSuspendAll();//禁止任务调度,确保IIC时序完整性
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
	if(xTaskResumeAll())//连接上下文
	 {
				taskYIELD();
	 }	
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}

OLED.h

#ifndef __OLED_H
#define __OLED_H

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "semphr.h"	
#include "task.h"
#include "queue.h"

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

#endif

OLED_Font.h

#ifndef __OLED_FONT_H
#define __OLED_FONT_H
#include "stm32f10x.h"

/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//  0
	
	0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
	
	0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
	
	0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
	0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
	
	0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
	0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
	
	0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
	0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
	
	0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
	0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
	
	0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
	
	0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
	0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
	
	0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
	0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
	
	0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
	0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
	
	0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
	0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
	
	0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
	0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
	
	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
	0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
	
	0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
	
	0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
	0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
	
	0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
	0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
	
	0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
	0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
	
	0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
	0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
	
	0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
	0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
	
	0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
	0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
	
	0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
	0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
	
	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
	0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
	
	0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
	0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
	
	0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
	0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
	
	0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
	0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
	
	0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
	0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
	
	0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
	0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
	
	0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
	0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
	
	0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
	0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32
	
	0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
	0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
	
	0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
	
	0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
	0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
	
	0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
	
	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
	0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
	
	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
	0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
	
	0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
	0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
	
	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
	0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
	
	0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
	
	0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
	0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
	
	0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
	0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
	
	0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
	
	0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
	0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
	
	0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
	0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
	
	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
	
	0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
	0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
	
	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
	
	0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
	0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
	
	0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
	0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
	
	0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
	0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
	
	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
	0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
	
	0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
	0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
	
	0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
	0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
	
	0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
	0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
	
	0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
	0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
	
	0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
	0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
	
	0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
	0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
	
	0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
	
	0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
	0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
	
	0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
	
	0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
	
	0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
	0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
	
	0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
	0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
	
	0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
	0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
	
	0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
	
	0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
	0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
	
	0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
	
	0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
	0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
	
	0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
	0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
	
	0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
	
	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
	0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
	
	0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,
	0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
	
	0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
	0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
	
	0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
	0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
	
	0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
	0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
	
	0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
	0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
	
	0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
	0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
	
	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
	0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
	
	0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
	0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
	
	0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
	0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
	
	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
	0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
	
	0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
	
	0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
	0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
	
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
	
	0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
	0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
	
	0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};

#endif

五、DHT11温湿度模块

1.dht11.c

PA6-VIVO

#include "stm32f10x.h"  // Device header
#include "SysTick.h"
#include "OLED.h"
#include "FreeRTOS.h"
#include "semphr.h"	
#include "task.h"
#include "queue.h"
#include "dht11.h"

#define DHT GPIO_Pin_6
#define DHTPROT GPIOA
#define OUT 1
#define INT 0

#define DHT_Low GPIO_ResetBits(DHTPROT,DHT)
#define DHT_High GPIO_SetBits(DHTPROT,DHT)

u8 time = 0;
extern QueueHandle_t  DHT11Queue;

void DHT11_UserConfig(void)
{
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
}


void DHT_Mobe(u8 mode)
{
		GPIO_InitTypeDef GPIO_InitStructure;
		if(mode)//1输出 
		{
				GPIO_InitStructure.GPIO_Pin = DHT;
				GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
				GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		}
		else//0输入
		{
				GPIO_InitStructure.GPIO_Pin = DHT;
				GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//DHT有上拉电阻
		}
		GPIO_Init(DHTPROT,&GPIO_InitStructure);
}

void DHT_Sends_Start(void)
{
		DHT_Mobe(OUT);//开启输出模式
		DHT_Low;
	  delay_ms(20);
		DHT_High;
		delay_us(40);
}

void DHT_Sends_Respooner(void)
{
		DHT_Mobe(INT);//开启输入模式,判断由DHT11控制总线的状态
		while((GPIO_ReadInputDataBit(DHTPROT,DHT)==0)&&(time<100))//等待DHT11响应信号结束
		{
				time++;
				delay_us(1);
				if(time>99)
				{
									printf("%s %d\r\n",__FUNCTION__,__LINE__);//打印所在的文件名和所在的行号
				}
		}
		time = 0;
		while((GPIO_ReadInputDataBit(DHTPROT,DHT)==1)&&(time<100))//等待DHT11拉高
		{
				time++;
				delay_us(1);
				if(time>99)
				{
									printf("%s %d\r\n",__FUNCTION__,__LINE__);
				}
		}

}
//读数据
u8 DHT11_Read_Byte(void)
{
	  u8 data = 0,i;
		for(i=0;i<8;i++)
		{
				data <<=1;
				DHT_Mobe(INT);//开启输入模式读取数据
						while((GPIO_ReadInputDataBit(DHTPROT,DHT)==0)&&(time<100))//50us等待
						{
								time++;
								delay_us(1);
						}
						time = 0;
						delay_us(40);
						if(GPIO_ReadInputDataBit(DHTPROT,DHT)==1)
						{
								data |= 0x01;
								while((GPIO_ReadInputDataBit(DHTPROT,DHT)==1)&&(time<100))//判断高电平是否结束
						{
								time++;
								delay_us(1);
						}
						time = 0;	
						}//0
		}
		return data;//返回5个字节
}

void DHT11_Read_Data(u8 *humi,u8 *temp)
{
		u8 arr[5] = {0,0,0,0,0},i,j;
		 
		DHT_Sends_Start();
		DHT_Sends_Respooner();
		for(i=0;i<5;i++)
		{
				arr[i]=DHT11_Read_Byte();
		}
		delay_us(70);
		if((arr[0]+arr[1]+arr[2]+arr[3]==arr[4]))//检验数据
		{
				*humi = arr[0];
				*temp = arr[2];
		}
		else//丢包
		{
				for(j=0;j<5;j++)
				{
					arr[j] = 0;
				}
		}
}

uint8_t humiReda = 0;
uint8_t tempReda = 0;
//读取数值
void DHT11_Red(void)
{
				DHT11_Read_Data(&humiReda,&tempReda);
				xQueueSend(DHT11Queue,&humiReda,portMAX_DELAY);//写入队列
				xQueueSend(DHT11Queue,&tempReda,portMAX_DELAY);				
				OLED_ShowString(2,1,"hemi=");
				OLED_ShowString(3,1,"temp=");
				OLED_ShowNum(2,6,humiReda,2);
				OLED_ShowNum(3,6,tempReda,2);
				debug_printf("hemi=%d temp=%d\r\n",humiReda,tempReda);
}

 dht11.h

#include "stm32f10x.h"                  // Device header
#include "bsp_usart.h"

void DHT11_Read_Data(u8 *humi,u8 *temp);
void DHT11_UserConfig(void);
void DHT11_Red(void);

六、硬件SPI1初始化

PA5------->SPI1-SCK

PA6------->SPI1-MISO

PA7------->SPI1-MOSI

PC13------->CS

 MySPI.c

#include "MySPI.h"

void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOC, GPIO_Pin_13, (BitAction)BitValue);
}

void MySPI_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);	
	
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//使用软件模拟cs
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_Cmd(SPI1, ENABLE);

	MySPI_W_SS(1);
}


void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}
/*
		SPI发送一个字节数据换取从机返回数据
*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
	
	SPI_I2S_SendData(SPI1, ByteSend);
	
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
	
	return SPI_I2S_ReceiveData(SPI1);		 
}

MySPI.h

#ifndef __MYSPI_H
#define __MYSPI_H

#include "stm32f10x.h"                  // Device header
#include "stm32f10x_spi.h" 

void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);

#endif

七、W25Q128外部Flash初始化

W25Q128.c

#include "MySPI.h"
#include "W25Q64_Ins.h"
#include "FreeRTOS.h"
#include "semphr.h"	
#include "task.h"
#include "queue.h"
#include "W25Q128.h"

/*页地址划分
	1.0x00-0xFF
	2.0x100 - 0x1FF
	3.0x300 - 0x3FF
	4.0x700 - 0x7FF
	5.0xF00 - 0xFFF
*/
 
void W25Q128_Init(void)
{
	MySPI_Init();
}

void W25Q128_ReadID(uint8_t *MID, uint16_t *DID)
{

	MySPI_Start();
	MySPI_SwapByte(W25Q64_JEDEC_ID);
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	*DID <<= 8;
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	MySPI_Stop();
	
}

void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}

void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
	Timeout = 100000;
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
	{
		Timeout --;
		if (Timeout == 0)
		{
			break;
		}
	}
	MySPI_Stop();
}

void W25Q128_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
		uint16_t i;
		vTaskSuspendAll();//禁止任务调度,确保SPI时序完整性
		W25Q64_WriteEnable();
		
		MySPI_Start();
		MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
		MySPI_SwapByte(Address >> 16);
		MySPI_SwapByte(Address >> 8);
		MySPI_SwapByte(Address);
		for (i = 0; i < Count; i ++)
		{
			MySPI_SwapByte(DataArray[i]);
		}
		MySPI_Stop();
		
		W25Q64_WaitBusy();
		if(xTaskResumeAll())//连接上下文
		{
					taskYIELD();
		}	
}

void W25Q128_SectorErase(uint32_t Address)
{
	vTaskSuspendAll();//禁止任务调度,确保SPI时序完整性
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	MySPI_Stop();
	
	W25Q64_WaitBusy();
	if(xTaskResumeAll())//连接上下文
	 {
				taskYIELD();
	 }	
}

void W25Q128_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	vTaskSuspendAll();//禁止任务调度,确保SPI时序完整性
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	}
	MySPI_Stop();
	if(xTaskResumeAll())//连接上下文
	{
				taskYIELD();
	}	
}

W25Q128.h

#ifndef __W25Q64_H
#define __W25Q64_H

#include "stm32f10x.h"                  // Device header

void W25Q128_Init(void);
void W25Q128_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q128_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q128_SectorErase(uint32_t Address);
void W25Q128_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);

#endif

W25Q64_Ins.h(指令库)

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H


#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF

#endif

八、定时器初始化(使其为FreeRTOS的时间统计提供时基)

时间统计功能需要用户提供一个高精度的时钟,单片机时钟周期为72MHZ,这里使用定时器 6,时钟的精度要比 FreeRTOS 的系统时钟高,大约 10~20 倍即可。在FreeRTOSConfig.h配置文件中可以看到FreeRTOS 系统时钟我们配置的是 1000HZ,周期 1ms,这里我们将定时器 6 的中断频率配置为 10KHZ,周期 0.1,刚好是系统时钟频率的10 倍

主频72 000 000 / 100 /72 = 10,000HZ

Tout= ((arr+1)(psc+1))/Tclk =     //72 * 100 /72 = 100(us) = 0.1ms

time.c

#include "time.h"
#include "bsp_usart.h"

/*******************************************************************************
* 函 数 名         : TIM6_Init
* 函数功能		   : TIM6初始化函数
* 输    入         : per:重装载值
					 psc:分频系数
* 输    出         : 无
*******************************************************************************/
void TIM6_Init(u16 per,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);//使能TIM3时钟
	
	TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
	TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure);
	
	TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); //开启定时器中断
	TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;//定时器中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=5;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_Cmd(TIM6,ENABLE); //使能定时器	
}

volatile uint32_t CPU_RunTime = 0UL;
/*******************************************************************************
* 函 数 名         : TIM6_IRQHandler
* 函数功能		   : TIM6中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void TIM6_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM6,TIM_IT_Update))
	{
		CPU_RunTime++;
	}
	TIM_ClearITPendingBit(TIM6,TIM_IT_Update);	
}

time.h

#ifndef _time_H
#define _time_H

#include "system.h"

void TIM6_Init(u16 per,u16 psc);

#endif

bsp_usart.h

#ifndef __BSP__USART_H
#define __BSP__USART_H

#include <stm32f10x.h>  
#include <stdio.h>
#include "string.h"      

/*****************************************************************************
 ** 移植配置
****************************************************************************/
// 用哪个串口与上位机通信,可自行
#define USARTx_DEBUG            USART1              // 用于重定向printf, 使printf通过USARTx发送数据
// 数据接收缓冲区大小,可自行修改
#define U1_RX_BUF_SIZE            1024              // 配置每个USARTx接收缓冲区的大小(字节数),包括中断里的缓存大小,和xUSART结构体里的缓存区大小
#define U3_RX_BUF_SIZE            1024              // --- 当每帧接收到的数据字节数,超过此值时,超出部分,将在中断中直接弃舍,直到接收结束(发生空闲中断); 


/*
		为确保串口发送时不被其他任务抢占夺取串口资源,使用暂停任务调度或信号量俩种锁,这里使用暂停任务调度
*/
#define debug_printf(format, args...)       do{                           \
                         vTaskSuspendAll();                     \
                         printf(format, ##args); \
												 if(xTaskResumeAll()){taskYIELD(); }}while(0)

//#define debug_printf(format, args...)       do{                           \
//                         if(xSemaphoreTake(xMutex, 100) == pdFALSE)   \
//                         return;                     \
//                         printf(format, ##args); \
//                         xSemaphoreGive(xMutex); }while(0)

typedef struct 
{
    uint8_t   USART1InitFlag;                       // 初始化标记; 0=未初始化, 1=已初始化
    uint16_t  USART1ReceivedNum;                    // 接收到多少个字节数据; 当等于0时,表示没有接收到数据; 当大于0时,表示已收到一帧新数据
    uint8_t   USART1ReceivedBuffer[U1_RX_BUF_SIZE]; // 接收到数据的缓存
    
    
    uint8_t   USART3InitFlag;                       // 初始化标记; 0=未初始化, 1=已初始化
    uint16_t  USART3ReceivedNum;                    // 接收到多少个字节数据; 当等于0时,表示没有接收到数据; 当大于0时,表示已收到一帧新数据
    char   USART3ReceivedBuffer[U3_RX_BUF_SIZE]; // 接收到数据的缓存 
   
    
    uint16_t  testCNT;                              // 仅用于测试
    
}xUSATR_TypeDef;

extern xUSATR_TypeDef  xUSART;                      // 声明为全局变量,方便记录信息、状态
    

// USART1
void    USART1_Init (uint32_t baudrate);                      // 初始化串口的GPIO、通信参数配置、中断优先级; (波特率可设、8位数据、无校验、1个停止位)
uint8_t USART1_GetBuffer (uint8_t* buffer, uint8_t* cnt);     // 获取接收到的数据
void    USART1_SendData (uint8_t* buf, uint8_t cnt);          // 通过中断发送数据,适合各种数据
void    USART1_SendString (char* stringTemp);                 // 通过中断发送字符串,适合字符串,长度在256个长度内的
void    USART1_printfForDMA (char* stringTemp) ;              // 通过DMA发送数据,适合一次过发送数据量特别大的字符串,省了占用中断的时间
// USART3
void    USART3_Init (uint32_t baudrate);                      // 初始化串口的GPIO、通信参数配置、中断优先级; (波特率可设、8位数据、无校验、1个停止位)
uint8_t USART3_GetBuffer (uint8_t* buffer, uint8_t* cnt);     // 获取接收到的数据
void    USART3_SendData (uint8_t* buf, uint8_t cnt);          // 通过中断发送数据,适合各种数据
void    USART3_SendString (char* stringTemp);                 // 通过中断发送字符串,适合字符串,长度在256个长度内的
void Receive_Task(void *pvParameters);
#endif

九、ESP8266初始化

bsp_ESP8266.c

#include "bsp_ESP8266.h"
#include "SysTick.h"

xESP8266_TypeDef  xESP8266;


// 向ESP8266模块发送AT命令
// 参数: char* cmdString :     AT命令字符串地址
//       char* answerString :  等待返回的命令确认字符串地址
//       uint32_t waitTimesMS: 发送命令后等待的时间,毫秒(非准确时间值)
uint8_t ESP8266_CMD(char *cmdString, char *answerString, uint32_t waitTimesMS)
{
    uint16_t CNT = 0;                   // 等待超时计数
    ESP8266_CleanReceivedFlag();        // 接收标示置0
    ESP8266_SendString(cmdString);      // 发送AT指令
    while (1)                           // 等待指令返回执行情况
    {
        if (ESP8266_CheckReceivedNum())
        {
            if (strstr((char *)xESP8266.ReceivedBuffer, answerString)) // 收到命令确认
                return 1;

        }
        delay_ms(1);
        if (++CNT > waitTimesMS)       // 超时未收到正确数据,
        {
            return 0;                  // 返回错误值:0

        }
    }
}


// ESP8266初始化
uint8_t ESP8266_Init(USART_TypeDef *USARTx, uint32_t baudrate)
{
    printf("\r\nESP8266 开始配置及测试......\r\n");
    delay_ms(300);                                    // 重要,上电后,必须稍延时以等待8266稳定方可工作

    if (USARTx == USART1)
    {
        USART1_Init(baudrate);
    }
    if (USARTx == USART3)
    {
        USART3_Init(baudrate);
    }


    xESP8266.FlagOkay = 0;           // 初始化状态
    xESP8266.USARTx   = USARTx;      // 记录所用串口端口
    xESP8266.Baudrate = baudrate;    // 记录所用的波特率

    ESP8266_CMD("AT\r\n",   "OK", 1500)  ? (xESP8266.FlagOkay = 1) : (xESP8266.FlagOkay = 0) ; // 测试是否能连接ESP8266
    xESP8266.FlagOkay ? printf("模块连接测试:  成功\r\n") : printf("模块连接测试:  失败\r\n");   // 输出连接状况

    return xESP8266.FlagOkay;      // 返回初始化状态,0_失败,1_正常
}


void ESP8266_SendString(char *str)
{
    if (xESP8266.USARTx == USART1)
    {
        USART1_SendString(str);
    }
    if (xESP8266.USARTx == USART3)
    {
        USART3_SendString(str);
    }
}

// 检查是否收到ESP8266发回来的数据,标志
// 返回接收到的字节数量
uint16_t ESP8266_CheckReceivedNum(void)
{
    if ((xESP8266.USARTx == USART1) && (xUSART.USART1ReceivedNum))
    {
        xESP8266.ReceivedNum = xUSART.USART1ReceivedNum;
        memcpy(xESP8266.ReceivedBuffer, xUSART.USART1ReceivedBuffer, xESP8266.ReceivedNum);
        xUSART.USART1ReceivedNum = 0;
    }
    if ((xESP8266.USARTx == USART3) && (xUSART.USART3ReceivedNum))
    {
        xESP8266.ReceivedNum = xUSART.USART3ReceivedNum;
        memcpy(xESP8266.ReceivedBuffer, xUSART.USART3ReceivedBuffer, xESP8266.ReceivedNum);
        xUSART.USART3ReceivedNum = 0;
    }


    return xESP8266.ReceivedNum;
}


// 清理ESP8266的接收缓存,包括接收长度变量和数据存放缓存
void ESP8266_CleanReceivedFlag(void)
{
    if (xESP8266.USARTx == USART1)
    {
        xUSART.USART1ReceivedNum = 0;
    }
    if (xESP8266.USARTx == USART3)
    {
        xUSART.USART3ReceivedNum = 0;
    }
    xESP8266.ReceivedNum = 0;                                         // 置0,接收长度
    memset(xESP8266.ReceivedBuffer, 0, ESP8266_RX_BUF_SIZE);          // 清零,接收缓存
}


/******************************************************************************
 * 函  数: ESP8266_GetLinkStatus
 * 功  能: 初始化USART的GPIO、通信参数配置、中断优先级
 *          (8位数据、无校验、1个停止位)
 * 参  数: uint32_t baudrate  通信波特率
 * 返回值:  0_获取状态失败
 *          2_获得ip
 *          3_建立连接
 *          4_失去连接
 ******************************************************************************/
uint8_t ESP8266_GetLinkStatus(void)
{
    if (ESP8266_CMD("AT+CIPSTATUS\r\n", "OK", 10000))
    {
        if (strstr((char *)xESP8266.ReceivedBuffer, "STATUS:2"))
        {
            ESP8266_CleanReceivedFlag();
            return 2;
        }
        if (strstr((char *)xESP8266.ReceivedBuffer, "STATUS:3"))
        {
            ESP8266_CleanReceivedFlag();
            return 3;
        }

        if (strstr((char *)xESP8266.ReceivedBuffer, "STATUS:4"))
        {
            ESP8266_CleanReceivedFlag();
            return 4;
        }
        if (strstr((char *)xESP8266.ReceivedBuffer, "STATUS:5"))
        {
            ESP8266_CleanReceivedFlag();
            return 5;
        }
    }
    
    ESP8266_CleanReceivedFlag();
    return 0;
}



/******************************************************************************
 * 函  数: ESP8266_JoinAP
 * 功  能: 连接AP
 * 参  数: char* SSID       WiFi名称
 *          char* passWord   WiFi密码
 * 返 回 值: 0_连接失败
 *           1_连接成功
 ******************************************************************************/
uint8_t ESP8266_JoinAP(char *SSID, char *passWord)
{
    char strTemp[60];
    uint8_t linkStatus = 0;

    // 把ESP8266重新配置成SAT模式
    ESP8266_CMD("AT+RESTORE\r\n", "ready", 3000)  ? printf("恢复出厂设置:  成功\r\n"): printf("恢复出厂设置: 失败\r\n");    // 恢复模块的出厂设置
    ESP8266_CMD("AT+CWMODE=1\r\n",    "OK", 3000)  ? printf("配置 STA模式:  成功\r\n") : printf("配置 STA模式: 失败\r\n");   // 工作模式:1_STA, 2_AP, 3_STA+AP
    ESP8266_CMD("AT+RST\r\n",      "ready", 3000)  ? printf("重启 ESP8266:  成功\r\n") : printf("重启 ESP8266: 失败\r\n");   // 重启模块: 设置工作模式后,需重启才生效
    ESP8266_CMD("AT+CIPMUX=0\r\n",    "OK", 3000)  ? printf("单连接模式  :  成功\r\n") : printf("单连接模式  : 失败\r\n");   // 多 连 接: 0_单连接,1_多连接
    //连接至指定WiFi热点
    printf("准备连接SSID: %s, %s\r\n", SSID, passWord);
    sprintf(strTemp, "AT+CWJAP=\"%s\",\"%s\"\r\n", SSID, passWord);
    printf("开始连接AP ... ");
    ESP8266_CMD(strTemp,        "OK\r\n", 20000)  ? printf("成功\r\n") : printf("失败\r\n");
    // 检测连接状态
    printf("获取连接状态:");
    linkStatus = ESP8266_GetLinkStatus();
    if (linkStatus == 0)
    {
        printf(" 失败,原因:获取失败!\r\n");
        return 0;
    }
    if (linkStatus == 2)
    {
        printf(" 成功,已获得IP\r\n");
        return 1;
    }
    if (linkStatus == 3)
    {
        printf(" 失败,原因:已连接,但未获得IP!\r\n");
        return 0;
    }
    if (linkStatus == 4)
    {
        printf(" 失败,原因:失去连接!\r\n");
        return 0;
    }
    if (linkStatus == 5)
    {
        printf(" 失败,原因:没有连接\r\n");
        return 0;
    }

    return 0;

}

// 把模块设置成AP模式
uint8_t ESP8266_SetAP(char *SSID, char *passWord)
{
    char strTemp[60];

    printf("准备建立SSID:%s, %s\r\n", SSID, passWord);
    // 把ESP8266重新配置成AP模式
    ESP8266_CMD("AT+RESTORE\r\n", "ready", 1000)  ? printf("恢复出厂设置: 成功\r\n") : printf("恢复出厂设置: 失败\r\n");   // 恢复模块的出厂设置
    ESP8266_CMD("AT+CWMODE=2\r\n",   "OK", 3000)  ? printf("配置为AP模式: 成功\r\n") : printf("配置 STA模式: 失败\r\n");   // 工作模式:1_STA, 2_AP, 3_STA+AP
    ESP8266_CMD("AT+RST\r\n",     "ready", 3000)  ? printf("重启 ESP8266: 成功\r\n") : printf("重启 ESP8266: 失败\r\n");   // 重启模块: 设置工作模式后,需重启才生效
    // 配置WiFi热点
    sprintf(strTemp, "AT+CWSAP=\"%s\",\"%s\",11,0\r\n", SSID, passWord);
    printf("开始建立AP... ");
    if (ESP8266_CMD(strTemp,        "OK\r\n", 10000))
    {
        printf("成功\r\n");
        return 1;
    }
    else
    {
        printf("失败\r\n");
        return 0;
    }
}


uint8_t ESP8266_TCPConnect(char *IP, uint16_t port)
{
    char strTemp[100];
    uint8_t flag = 0;

    printf("建立TCP通信... ");
    sprintf(strTemp, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", IP, port);
    ESP8266_CMD(strTemp, "OK\r\n", 20000)  ? (flag = 1) : (flag = 0) ;
    flag ? printf("成功\r\n") : printf("失败, 请检查APP:IP地址、端口号、是否已断开旧的连接状态\r\n");
    ESP8266_CleanReceivedFlag();
    return flag;
}

// 设置:多连接,0_单连接,1_多连接;备注:透传必须AT+CIPMUX=0;
uint8_t   ESP8266_SetCIPMux(uint8_t value)
{
    uint8_t flag = 0;
    char strTemp[50];
    sprintf(strTemp, "AT+CIPMUX=%d\r\n", value);
    ESP8266_CMD(strTemp, "OK", 3000)  ? (flag = 1) : (flag = 0) ;
    flag ? printf("设置连接方式:  成功\r\n") : printf("设置连接方式: 失败\r\n");   // 多 连 接: 0_单连接,1_多连接
    ESP8266_CleanReceivedFlag();
    return flag;
}


// 设置:传输方式,0_普通传输,1_透明传输
uint8_t   ESP8266_SetCIPMode(uint8_t value)
{
    uint8_t flag = 0;
    char strTemp[50];
    sprintf(strTemp, "AT+CIPMODE=%d\r\n", value);
    ESP8266_CMD(strTemp, "OK", 3000)  ? (flag = 1) : (flag = 0) ;
    flag ? printf("设置传输方式:  成功\r\n") : printf("设置传输方式: 失败\r\n");   // 传输方式,0_普通传输,1_透明传输
    ESP8266_CleanReceivedFlag();
    return flag;
}

// 开启透传
uint8_t ESP8266_EnablePassThrough(void)
{
    uint8_t status = 1;
    status &= ESP8266_CMD("AT+CIPMODE=1\r\n", "OK", 3000);   // 传输方式,0_普通传输,1_透明传输
    status &= ESP8266_CMD("AT+CIPSEND\r\n",    ">", 3000);   // 数据传输,当已设置透传时,无需参数

    ESP8266_CleanReceivedFlag();                             // 清除接收标记、缓存

    status ? printf("打开透明传输:  成功\r\n") : printf("打开透明传输:  失败\r\n");
    return status;
}

// 停止透传
uint8_t ESP8266_DisablePassThrough(void)
{
    ESP8266_SendString("+++");
    printf("已关闭透明传输!\r\n");
    return 0;
}

bsp_ESP8266.h

#ifndef __ESP8266__H
#define __ESP8266__H

#include <stm32f10x.h>  
#include <stdio.h>
#include "bsp_usart.h"

#define ESP8266_RX_BUF_SIZE       1200                    // 数据接收缓冲区大小,大部份情况下都不用修改; USART中断里的缓存大小

typedef struct 
{    
    uint8_t         FlagOkay;                             // 状态标记; 0=未初始化或异常, 1=正常
    uint16_t        ReceivedNum;                          // 接收到多少个字节数据; 0-无数据,非0_接收到的字节数
    uint8_t         ReceivedBuffer[ESP8266_RX_BUF_SIZE];  // 接收到数据的缓存; ESP8266在AT模式下,每帧数据最长为1056个字节;
    char*           APName;                               // 当创建或加入AP时的: SSID
    char*           APPassword;                           // 当创建或加入AP时的: 密码
    uint32_t        Baudrate;                             // 记录所用的串口波特率
    USART_TypeDef*  USARTx;                               // 记录所用的端口
}xESP8266_TypeDef;
extern xESP8266_TypeDef  xESP8266;                        // 声明为全局变量,方便记录信息、状态
    

// 下面4个基本函数,可完成AT命令下绝大部分操作
uint8_t   ESP8266_Init(USART_TypeDef* USARTx, uint32_t baudrate);  // 初始化
void      ESP8266_SendString(char* str);                           // 发送任意长度字符串
uint16_t  ESP8266_CheckReceivedNum(void);                          // 检查是否收到新数据,返回接收到的字节长度
void      ESP8266_CleanReceivedFlag(void);                         // 清理ESP8266的接收缓存,包括接收长度变量和数据存放缓存
// 下面4个扩展函数,为方便使用而编写,基于上面4个函数实现
uint8_t   ESP8266_JoinAP (char* SSID, char* passWord);             // 加入某热点,
uint8_t   ESP8266_SetAP(char* SSID, char* passWord);               // 把模块配置成AP模式 
uint8_t   ESP8266_GetLinkStatus(void);                             // 获取连接状态
uint8_t   ESP8266_TCPConnect(char* IP, uint16_t port);             // 以TCP通信方式与目标连接
// 常用AT指令封装
uint8_t   ESP8266_SetCIPMux(uint8_t value);                        // 设置:多连接,0_单连接,1_多连接;备注:透传必须AT+CIPMUX=0;
uint8_t   ESP8266_SetCIPMode(uint8_t value);                       // 设置:传输方式,0_普通传输,1_透明传输
uint8_t   ESP8266_CMD (char* cmdString, char* answerString, uint32_t waitTimesMS); // 发送AT命令,并等待返回
uint8_t   ESP8266_EnablePassThrough(void);
uint8_t   ESP8266_DisablePassThrough(void);
#endif


十、KEY初始化

KEY1------->PA0

KEY2------->PA1

KEY3------->PA4

 bsp_key.c

#include "bsp_key.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"

extern TaskHandle_t KEY_SuotPut_task_Handler;
extern TaskHandle_t ReadHistoricalData_task_Handler;

void Key_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;    // 作用:配置引脚工作模式
    NVIC_InitTypeDef NVIC_InitStruct ;      // 作用:配置优先级
    EXTI_InitTypeDef EXTI_InitStruct ;      // 作用:配置引脚中断方式

    // 使能AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) ;      // EXTI的时钟要设置AFIO寄存器

    // 使能GPIO时钟, 为减少调试过程忘了使能时钟而出错,把相关GPIO端口时钟,都使能了;
    RCC->APB2ENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOF ;

    // KEY_1
    // 配置引脚工作模式
    GPIO_InitStructure.GPIO_Pin  = KEY_1_PIN;                  // 引脚KEY_1, 闲时下拉(重要), 按下时被置高电平
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD ;             // 引脚工作模式; 闲时电平状态(使用芯片内部电阻进行电平上下拉)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz ;         // 输出电平反转速度;在输入状态时无效,但GPIO_Init函数需要用到;
    GPIO_Init(KEY_1_GPIO, &GPIO_InitStructure);                // 初始化,调用引脚工作模式配置函数
    // 配置中断的优先级
    NVIC_InitStruct.NVIC_IRQChannel = KEY_1_IRQN ;             // 中断号
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 7 ;    // 配置抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ;           // 配置子优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;              // 使能中断通道
    NVIC_Init(&NVIC_InitStruct) ;                              // 初始化,调用优先级配置函数
    // 配置中断的方式
    GPIO_EXTILineConfig(KEY_1_GPIOSOURCE, KEY_1_PINSOURCE);    // 选择作为EXTI线的GPIO引脚
    EXTI_InitStruct.EXTI_Line    = KEY_1_EXTI_LINE ;           // 配置中断or事件线
    EXTI_InitStruct.EXTI_Mode    = EXTI_Mode_Interrupt ;       // 模式:中断:EXTI_Mode_Interrupt、事件:EXTI_Mode_Event
    EXTI_InitStruct.EXTI_Trigger = KEY_1_TRIM ;                // 边沿触发: 上升:EXTI_Trigger_Rising 、下降:EXTI_Trigger_Falling 、浮空:EXTI_Trigger_Rising_Falling
    EXTI_InitStruct.EXTI_LineCmd = ENABLE ;                    // 使能EXTI线
    EXTI_Init(&EXTI_InitStruct) ;                              // 初始化,调用中断线配置函数

    // KEY_2
    // 配置引脚工作模式
    GPIO_InitStructure.GPIO_Pin  = KEY_2_PIN;                  // 引脚KEY_1, 闲时下拉(重要), 按下时被置高电平
    GPIO_InitStructure.GPIO_Mode = KEY_2_MODE ;                // 引脚工作模式; 闲时电平状态(使用芯片内部电阻进行电平上下拉)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz ;         // 输出电平反转速度;在输入状态时无效,但GPIO_Init函数需要用到;
    GPIO_Init(KEY_2_GPIO, &GPIO_InitStructure);                // 初始化,调用引脚工作模式配置函数
    // 配置中断的优先级
    NVIC_InitStruct.NVIC_IRQChannel = KEY_2_IRQN ;             // 中断号
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 7 ;    // 配置抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;           // 配置子优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;              // 使能中断通道
    NVIC_Init(&NVIC_InitStruct) ;                              // 初始化,调用优先级配置函数
    // 配置中断的方式
    GPIO_EXTILineConfig(KEY_2_GPIOSOURCE, KEY_2_PINSOURCE);    // 选择作为EXTI线的GPIO引脚
    EXTI_InitStruct.EXTI_Line    = KEY_2_EXTI_LINE ;           // 配置中断or事件线
    EXTI_InitStruct.EXTI_Mode    = EXTI_Mode_Interrupt ;       // 模式:中断:EXTI_Mode_Interrupt、事件:EXTI_Mode_Event
    EXTI_InitStruct.EXTI_Trigger = KEY_2_TRIM ;                // 边沿触发: 上升:EXTI_Trigger_Rising 、下降:EXTI_Trigger_Falling 、浮空:EXTI_Trigger_Rising_Falling
    EXTI_InitStruct.EXTI_LineCmd = ENABLE ;                    // 使能EXTI线
    EXTI_Init(&EXTI_InitStruct) ;                              // 初始化,调用中断线配置函数

    // KEY_3
    // 配置引脚工作模式
    GPIO_InitStructure.GPIO_Pin  = KEY_3_PIN;                  // 引脚KEY_1, 闲时下拉(重要), 按下时被置高电平
    GPIO_InitStructure.GPIO_Mode = KEY_3_MODE ;                // 引脚工作模式; 闲时电平状态(使用芯片内部电阻进行电平上下拉)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz ;         // 输出电平反转速度;在输入状态时无效,但GPIO_Init函数需要用到;
    GPIO_Init(KEY_3_GPIO, &GPIO_InitStructure);                // 初始化,调用引脚工作模式配置函数
    // 配置中断的优先级
    NVIC_InitStruct.NVIC_IRQChannel = KEY_3_IRQN ;             // 中断号
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 7 ;    // 配置抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ;           // 配置子优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;              // 使能中断通道
    NVIC_Init(&NVIC_InitStruct) ;                              // 初始化,调用优先级配置函数
    // 配置中断的方式
    GPIO_EXTILineConfig(KEY_3_GPIOSOURCE, KEY_3_PINSOURCE);    // 选择作为EXTI线的GPIO引脚
    EXTI_InitStruct.EXTI_Line    = KEY_3_EXTI_LINE ;           // 配置中断or事件线
    EXTI_InitStruct.EXTI_Mode    = EXTI_Mode_Interrupt ;       // 模式:中断:EXTI_Mode_Interrupt、事件:EXTI_Mode_Event
    EXTI_InitStruct.EXTI_Trigger = KEY_3_TRIM ;                // 边沿触发: 上升:EXTI_Trigger_Rising 、下降:EXTI_Trigger_Falling 、浮空:EXTI_Trigger_Rising_Falling
    EXTI_InitStruct.EXTI_LineCmd = ENABLE ;                    // 使能EXTI线
    EXTI_Init(&EXTI_InitStruct) ;                              // 初始化,调用中断线配置函数

    printf("按键 初始化           配置完成\r");
}

// KEY_1 中断服务函数
void EXTI0_IRQHandler(void)                   
{
		BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (EXTI->PR & KEY_1_PIN)                      // 按键已使用电容作简单的硬件消抖
    {
        EXTI->PR |= KEY_1_PIN  ;                   // 清理中断标示
				vTaskNotifyGiveFromISR (KEY_SuotPut_task_Handler,&xHigherPriorityTaskWoken);//通知处理任务
	    	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//上下文切换
				//debug_printf("Receive1_Task_Handle 任务通知发送成功!\r\n");			
    }
}



// KEY_2 中断服务函数
void EXTI1_IRQHandler(void)                        
{
		BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (EXTI->PR & KEY_2_PIN)                      // 板子上的按键已使用电容作简单的硬件消抖,无需再使用软件延时消抖
    {
        EXTI->PR |= KEY_2_PIN  ;                   // 清理中断标示
				vTaskNotifyGiveFromISR (ReadHistoricalData_task_Handler,&xHigherPriorityTaskWoken);//通知处理任务
	    	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);		
    }
}


// KEY_3 中断服务函数
void EXTI4_IRQHandler(void)                
{
    if (EXTI->PR & KEY_3_PIN)                      // 板子上的按键已使用电容作简单的硬件消抖,无需再使用软件延时消抖
    {
        EXTI->PR |= KEY_3_PIN  ;                   // 清理中断标示 

    }
}







bsp_key.h

#ifndef _BSP_KEY_H
#define _BSP_KEY_H
#include <stm32f10x.h>  



/*****************************************************************************
 ** 移植时修改
****************************************************************************/
// KEY_1_WKUP, 闲时下拉,按下时被置高电平
#define KEY_1_GPIO                GPIOA                  // 所用端口
#define KEY_1_PIN                 GPIO_Pin_0             // 引脚编号
#define KEY_1_MODE                GPIO_Mode_IPD          // 引脚工作模式
#define KEY_1_TRIM                EXTI_Trigger_Rising    // 下降沿触发:EXTI_Trigger_Falling;   上升沿触发:EXTI_Trigger_Rising
#define KEY_1_IRQN                EXTI0_IRQn             // 中断向量编号
#define KEY_1_EXTI_LINE           EXTI_Line0             // 中断线编号
#define KEY_1_GPIOSOURCE          GPIO_PortSourceGPIOA   // 中断线端口
#define KEY_1_PINSOURCE           GPIO_PinSource0    
// KEY_2, 闲时上拉,按下时被置低电平
#define KEY_2_GPIO                GPIOA                  // 所用端口
#define KEY_2_PIN                 GPIO_Pin_1             // 引脚编号
#define KEY_2_MODE                GPIO_Mode_IPU          // 引脚工作模式
#define KEY_2_TRIM                EXTI_Trigger_Falling   // 下降沿触发:EXTI_Trigger_Falling;   上升沿触发:EXTI_Trigger_Rising
#define KEY_2_IRQN                EXTI1_IRQn             // 中断向量编号
#define KEY_2_GPIOSOURCE          GPIO_PortSourceGPIOA   // 中断线端口
#define KEY_2_EXTI_LINE           EXTI_Line1             // 中断线编号
#define KEY_2_PINSOURCE           GPIO_PinSource1   
// KEY_2, 闲时上拉,按下时被置低电平
#define KEY_3_GPIO                GPIOA                  // 所用端口
#define KEY_3_PIN                 GPIO_Pin_4             // 引脚编号
#define KEY_3_MODE                GPIO_Mode_IPU          // 引脚工作模式
#define KEY_3_TRIM                EXTI_Trigger_Falling   // 下降沿触发:EXTI_Trigger_Falling;   上升沿触发:EXTI_Trigger_Rising
#define KEY_3_IRQN                EXTI4_IRQn             // 中断向量编号
#define KEY_3_GPIOSOURCE          GPIO_PortSourceGPIOA   // 中断线端口
#define KEY_3_EXTI_LINE           EXTI_Line4             // 中断线编号
#define KEY_3_PINSOURCE           GPIO_PinSource4    



/*****************************************************************************
 ** 声明全局函数
****************************************************************************/
void Key_Init(void);



#endif

十一、任务功能介绍

1.任务句柄

TaskHandle_t StartTask_Handler;//初始任务句柄
TaskHandle_t esp8266Task_Handler;//ESP8266TPC连接任务句柄
TaskHandle_t DHT11_task_Handler;//读取DHT11任务句柄
TaskHandle_t led_task_Handler;//LED任务句柄
TaskHandle_t Receive_Task_Handler;//读取ESP8266下发任务句柄
TaskHandle_t ReadHistoricalData_task_Handler;//读取历史数据任务句柄
TaskHandle_t ReadW25Q128_task_Handler;//实时读取外部Flash任务句柄
TaskHandle_t KEY_SuotPut_task_Handler;//等待按键一任务通知任务句柄
TaskHandle_t CPU_task_Handler;//CPU统计任务信息句柄

2.信号量、任务通知返回值、队列、定时器句柄

SemaphoreHandle_t  uartSemaphore;//二进制禁止信号量句柄
BaseType_t xReturn = pdPASS;//任务通知
QueueHandle_t  uartQueue;//串口3接收队列
QueueHandle_t  DHT11Queue;//DHT11接收队列
TimerHandle_t xMyHandleTask1;

3.模块系统初始化

		SysTick_Init(72);//FreeRTOS延时初始化
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
		OLED_Init();
		USART1_Init(115200);
		printf("串口初始化成功\r\n");
		LED_Init();
		Key_Init();
		DHT11_UserConfig();
		W25Q128_Init();		
		TIM6_Init(100-1,72-1);//定时0.1ms

4.创建初始任务

		//创建开始任务
			xTaskCreate((TaskFunction_t )start_task,            //任务函数
									(const char*    )"start_task",          //任务名称
									(uint16_t       )100,        //任务堆栈大小
									(void*          )NULL,                  //传递给任务函数的参数
									(UBaseType_t    )1,       //任务优先级
									(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
			vTaskStartScheduler();          //开启任务调度

5.由初始任务再创建模块任务等

void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    uartSemaphore = xSemaphoreCreateBinary(); //创建串口接收完成锁 
		uartQueue = xQueueCreate( 1, sizeof(char *) );//串口发送队列
		DHT11Queue = xQueueCreate(5,sizeof(u8) * 2);//温湿度数据队列
		xMyHandleTask1 = xTimerCreate("mytimer",5000,pdTRUE,NULL,MyTimerCallbackFunction);//创建自动加载软件定时器 5000Tick触发一次
	
    xTaskCreate((TaskFunction_t )ESP8266_task,     
                (const char*    )"esp8266Start_task",   
                (uint16_t       )100, 
                (void*          )NULL,
                (UBaseType_t    )3,
                (TaskHandle_t*  )&esp8266Task_Handler); 
	  xTaskCreate(Receive_Task,"Receive_Task",100,NULL,2,&Receive_Task_Handler);	
		xTaskCreate(led_task,"led_task",100,NULL,1,&led_task_Handler);	
		xTaskCreate(DHT11_task,"DHT11_task",100,NULL,1,&DHT11_task_Handler);
		xTaskCreate(ReadHistoricalData_task,"ReadHistoricalData_task",200,NULL,1,&ReadHistoricalData_task_Handler);
		xTaskCreate(ReadW25Q128_task,"ReadW25Q128_task",100,NULL,1,&ReadW25Q128_task_Handler);
		xTaskCreate(KEY_SuotPut_task,"KEY_SuotPut_task",100,NULL,1,&KEY_SuotPut_task_Handler);
		xTaskCreate(CPU_task,"CPU_task",500,NULL,1,&CPU_task_Handler);
		vTaskSuspend(DHT11_task_Handler);//临时睡眠等待唤醒		
		vTaskSuspend(ReadW25Q128_task_Handler);									
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

ESP8266TPC连接任务,连接参数都在上方的宏定义上,需与手机APP的IP和端口一致,同时任务通知串口解析服务器下发数据函数及LED闪烁创建软件定时器使他们从biocked状态转变为ready状态

#define ServerIP "192.168.1.109"
#define Port 1234
#define SSID "304"
#define WIFIPassword "1234567890"
void ESP8266_task(void *pvParameters)
{
    while(1)
    {
			  debug_printf("任务一在运行中\r\n");	 
				ESP8266_Init(USART3, 115200);                      // 初始化;    参数:串口端口,波特率     注意:波特率默认为115200
				ESP8266_JoinAP(SSID, WIFIPassword);     // 加入WiFi;  参数: SSID, Password;      注意:ESP8266、手机所加入的Wifi是同一个AP(路由)
				ESP8266_TCPConnect(ServerIP, Port);          // 建立连接;  参数: 目标IP地址,端口号;  注意:这个IP和端口号,在目标APP“TCP连接”界面的正上方有显示
				ESP8266_EnablePassThrough();                      // 开始透明传输		
			  xReturn = xTaskNotifyGive(Receive_Task_Handler);
			  xReturn = xTaskNotifyGive(led_task_Handler);					
				if( xReturn == pdTRUE )
				{
						debug_printf("Receive1_Task_Handle 任务通知发送成功!\r\n");
						
				}	
				vTaskDelete(NULL); //删除开始任务			
		 }
}

读取ESP8266下发任务,该函数在bsp_usart.c中编写,主要是在USART3空闲中断触发后表示一帧数据接收成功,使用信号量来上锁,读取完成后再释放锁,并写入队列进行传输

 空闲中断

    // 空闲中断, 用于配合接收中断,以判断一帧数据的接收完成
    if (USART3->SR & (1 << 4))                                       // 检查IDLE(空闲中断标志位); IDLE中断标志清理方法:序列清零,USART1 ->SR;  USART1 ->DR;
    {
				if(uartSemaphore!=NULL)
				{					
					//释放二值信号量
					xSemaphoreGiveFromISR(uartSemaphore,&xHigherPriorityTaskWoken);	//上锁
				}
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换			  
        xUSART.USART3ReceivedNum  = 0;                               // 把接收到的数据字节数清0
        memcpy(xUSART.USART3ReceivedBuffer, RxTemp, U3_RX_BUF_SIZE); // 把本帧接收到的数据,存放到全局变量xUSART.USARTxReceivedBuffer中, 等待处理; 注意:复制的是整个数组,包括0值,以方便字符串数据
				buffer = xUSART.USART3ReceivedBuffer;
				xQueueSendToBackFromISR( uartQueue, &buffer,&QueuePriorityTaskWoken); //将数据写入队列
				portYIELD_FROM_ISR(QueuePriorityTaskWoken);	//上下文切换			
				xUSART.USART3ReceivedNum  = cnt;                             // 把接收到的字节数,存放到全局变量xUSART.USARTxReceivedCNT中;
				cnt = 0;                                                     // 接收字节数累计器,清零; 准备下一次的接收
        memset(RxTemp, 0, U3_RX_BUF_SIZE);                           // 接收数据缓存数组,清零; 准备下一次的接收
        USART3 ->SR;
        USART3 ->DR;                                                 // 清零IDLE中断标志位!! 序列清零,顺序不能错!!
    }

 接收任务

/*
		功能:解析服务器下发数据,服务器不可使用中文下发数据,可使用数字及英文
*/
void Receive_Task(void *pvParameters)
{

	BaseType_t err = pdFALSE;
	BaseType_t xStatus;
	char *buffer;
	
	ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待初始化完成后唤醒任务,过滤第一次接收
	debug_printf("Receive_Task!\n\n");	
	xSemaphoreTake(uartSemaphore,portMAX_DELAY);	//获取信号量
	xStatus = xQueueReceive( uartQueue, &buffer, portMAX_DELAY );
	
	while(1)
	{
		err=xSemaphoreTake(uartSemaphore,portMAX_DELAY);	//获取空闲中断信号量
		if(err==pdTRUE)							//获取信号量成功
		{  
				debug_printf("获取信号量成功\r\n");
		}
		xStatus = xQueueReceive( uartQueue, &buffer, portMAX_DELAY ); /* 只需要写入4字节, 无需写入整个buffer */
		if(xStatus==pdTRUE && xUSART.USART3ReceivedNum > 0)
		{
				debug_printf("成功获取队列:%s\r\n",buffer);
				OLED_ShowString(1,1,(char *)buffer);  
		}
    if (strstr((char *)buffer, "LED20"))	LED2=0;//服务器下发控制解析指令
    if (strstr((char *)buffer, "LED21"))  LED2=1;	
		xUSART.USART3ReceivedNum = 0;                                          // 接收标记置0                                                        // 返回所接收到新数据的字节数
		memset(xUSART.USART3ReceivedBuffer,0,sizeof(xUSART.USART3ReceivedBuffer));		
	}
}

 LED初始化任务及启动定时器

void led_task(void *pvParameters)
{
		ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待任务通知
		xTimerStart(xMyHandleTask1, 0);//启动软件定时器
//		W25Q128_SectorErase(0x100);//清空w25q128第二页地址数据
    while(1)
    {
        LED1=0;
        vTaskDelay(500);
        LED1=1;
        vTaskDelay(500);
				//debug_printf("task tow is runing to\r\n");
    }
}

DHT11读取任务,读取函数里有写入队列方便其它任务读取,并使用任务通知让其它任务从Blocked转从Ready状态

/*
		功能:读取温湿度数据把数据写入队列
*/
void DHT11_task(void *pvParameters)
{	 
    while(1)
    {	
				ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待按键任务锁
				DHT11_Red();				
				vTaskDelay(100);
		}
}

读取外部Flash中的历史写入数据,历史数据主要是由软件定时器每个周期采集的数据采样,按下按键二一次性读取一页,也同样上了锁

/*
		功能:按下KEY2按键读取历史数据,将外部Flash第二页一整页读取
*/

static int count=256;
void ReadHistoricalData_task(void *pvParameters)//读取队列中的数据
{
		uint8_t  HistoricalData[2],j;
		while(1)
		{
					ulTaskNotifyTake(pdTRUE,portMAX_DELAY);		
					for(j=0;j<127;j++)
					{
							W25Q128_ReadData(count, HistoricalData, 2);
							debug_printf("historical data humi=%d temp=%d\r\n",HistoricalData[0],HistoricalData[1]);	
							count+=2;
					}
					if(count>511)//防止读取外部Flash超过第三页
					{
								count = 256;
					}	
		}
}

通过按键一通知唤醒温湿度读取写入外部Flash,并向服务器上发一次数据,通知唤醒读取外部Flash读取任务

/*
		功能:读出按键按下缓存的数据		
*/

void ReadW25Q128_task(void *pvParameters)
{
		uint8_t ArrayRead[2];	
    while(1)
    {
				ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
				W25Q128_ReadData(0x000000, ArrayRead, 2);
				OLED_ShowNum(3, 3, ArrayRead[0], 2);
				OLED_ShowNum(3, 6, ArrayRead[1], 2);
				debug_printf("读出数据=%d %d\r\n",ArrayRead[0],ArrayRead[1]);	 
		}
}
/*
		功能:	当按下按键一的时候任务通知锁打开,通知唤醒温湿度读取任务,写入外部Flash
		通知唤醒读取外部Flash读取任务
*/

void KEY_SuotPut_task(void *pvParameters)
{
			uint8_t ArrayWrite[2];
			uint8_t temp=0,humi=0;
			char Dht11Dtat[10];
	
			while(1)
			{
					ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
					xTaskNotifyGive(DHT11_task_Handler);		
					vTaskResume(DHT11_task_Handler);
					xQueueReceive(DHT11Queue,&humi,portMAX_DELAY);
					xQueueReceive(DHT11Queue,&temp,portMAX_DELAY);
					ArrayWrite[0] = humi;
					ArrayWrite[1] = temp;					
					sprintf(Dht11Dtat, "humi=%d temp=%d\r\n", humi,temp);
					USART3_SendString(Dht11Dtat);	//向服务器发送一次数据
					OLED_ShowNum(2, 3, ArrayWrite[0], 2);
					OLED_ShowNum(2, 6, ArrayWrite[1], 2);	
					W25Q128_SectorErase(0x00000);//擦除扇区 
					W25Q128_PageProgram(0x00000, ArrayWrite, 2);  
					debug_printf("发送数据=%d %d\r\n",ArrayWrite[0],ArrayWrite[1]);	 
					xTaskNotifyGive(ReadW25Q128_task_Handler);	
					vTaskResume(ReadW25Q128_task_Handler);
			}
}

软件定时器回调函数,当设置的定时器Tick时钟到来的时候进入,读取温湿度数据并向服务器发送一次,创建时设置的是5000个Tick,主要是由读取温湿度上发及写入到外部Flash历史数据,通知执行一次打印CPU信息

/*
		软件定时器回调函数,当设置的定时器Tick时钟到来的时候进入,读取温湿度数据并向服务器发送一次
*/
void MyTimerCallbackFunction( TimerHandle_t xTimer)
{
	DHT11_Red();	
	xQueueReceive(DHT11Queue,&ArrayWrite[0],portMAX_DELAY);	
	xQueueReceive(DHT11Queue,&ArrayWrite[1],portMAX_DELAY);
	W25Q128_PageProgram(cnt, ArrayWrite, 2);	
	sprintf(Dht11HistData, "humi=%d temp=%d\r\n", ArrayWrite[0],ArrayWrite[1]);
	USART3_SendString(Dht11HistData);	
	printf("MyTimerCallbackFunction_t cnt = %d humi=%d temp= %d\r\n", cnt,ArrayWrite[0],ArrayWrite[1]);
	cnt +=2;
	if(cnt>511)//防止写入外部Flash超过第三页
	{
				cnt = 256;
				W25Q128_SectorErase(256);	
	}	
	//xTaskNotifyGive(CPU_task_Handler);//使用该函数即可打印CPC占用率
}

CPU信息打印函数,由软件定时器回调函数出发后,使该任务从阻塞状态转化为就绪状态

//CPU任务函数
void CPU_task(void *pvParameters)
{
  uint8_t CPU_RunInfo[400];//保存任务运行时间信息
	
	while(1)
  {
		ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待按键任务锁
		memset(CPU_RunInfo,0,400);//信息缓冲区清零
		vTaskList((char *)&CPU_RunInfo);  //获取任务运行时间信息
		
		debug_printf("---------------------------------------------\r\n");
		debug_printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");
		debug_printf("%s", CPU_RunInfo);
		debug_printf("---------------------------------------------\r\n");

		memset(CPU_RunInfo,0,400);				//信息缓冲区清零

		vTaskGetRunTimeStats((char *)&CPU_RunInfo);

		debug_printf("任务名       运行计数         利用率\r\n");
		debug_printf("%s", CPU_RunInfo);
		debug_printf("---------------------------------------------\r\n\n");
   }
}

十二、完整的main.c函数

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "OLED.h"
#include "semphr.h"
#include "bsp_ESP8266.h"
#include "bsp_key.h"
#include "dht11.h"
#include "W25Q128.h"
#include "timers.h"
#include "time.h"

#define ServerIP "192.168.1.109"
#define Port 1234
#define SSID "304"
#define WIFIPassword "1234567890"

TaskHandle_t StartTask_Handler;//初始任务句柄
TaskHandle_t esp8266Task_Handler;//ESP8266TPC连接任务句柄
TaskHandle_t DHT11_task_Handler;//读取DHT11任务句柄
TaskHandle_t led_task_Handler;//LED任务句柄
TaskHandle_t Receive_Task_Handler;//读取ESP8266下发任务句柄
TaskHandle_t ReadHistoricalData_task_Handler;//读取历史数据任务句柄
TaskHandle_t ReadW25Q128_task_Handler;//实时读取外部Flash任务句柄
TaskHandle_t KEY_SuotPut_task_Handler;//等待按键一任务通知任务句柄
TaskHandle_t CPU_task_Handler;//CPU统计任务信息句柄

SemaphoreHandle_t  uartSemaphore;//二进制禁止信号量句柄
BaseType_t xReturn = pdPASS;//任务通知
QueueHandle_t  uartQueue;//串口3接收队列
QueueHandle_t  DHT11Queue;//DHT11接收队列
TimerHandle_t xMyHandleTask1;

//任务函数
void start_task(void *pvParameters);
void ESP8266_task(void *pvParameters);
void DHT11_task(void *pvParameters);
void led_task(void *pvParameters);
void ReadHistoricalData_task(void *pvParameters);
void ReadW25Q128_task(void *pvParameters);
void KEY_SuotPut_task(void *pvParameters);
void CPU_task(void *pvParameters);

int main()
{
		SysTick_Init(72);//FreeRTOS延时初始化
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
		OLED_Init();
		USART1_Init(115200);
		printf("串口初始化成功\r\n");
		LED_Init();
		Key_Init();
		DHT11_UserConfig();
		W25Q128_Init();		
		TIM6_Init(100-1,72-1);//定时0.1ms

		//创建开始任务
			xTaskCreate((TaskFunction_t )start_task,            //任务函数
									(const char*    )"start_task",          //任务名称
									(uint16_t       )100,        //任务堆栈大小
									(void*          )NULL,                  //传递给任务函数的参数
									(UBaseType_t    )1,       //任务优先级
									(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
			vTaskStartScheduler();          //开启任务调度
		while(1)
		{		
					printf("运行出错");
		}
}

static int cnt = 256;//w25q128第二页首地址
uint8_t ArrayWrite[2];//临时缓冲区
char Dht11HistData[15];//上发服务器数组

/*
		软件定时器回调函数,当设置的定时器Tick时钟到来的时候进入,读取温湿度数据并向服务器发送一次
*/
void MyTimerCallbackFunction( TimerHandle_t xTimer)
{
	DHT11_Red();	
	xQueueReceive(DHT11Queue,&ArrayWrite[0],portMAX_DELAY);	
	xQueueReceive(DHT11Queue,&ArrayWrite[1],portMAX_DELAY);
	W25Q128_PageProgram(cnt, ArrayWrite, 2);	
	sprintf(Dht11HistData, "humi=%d temp=%d\r\n", ArrayWrite[0],ArrayWrite[1]);
	USART3_SendString(Dht11HistData);	
	printf("MyTimerCallbackFunction_t cnt = %d humi=%d temp= %d\r\n", cnt,ArrayWrite[0],ArrayWrite[1]);
	cnt +=2;
	if(cnt>511)//防止写入外部Flash超过第三页
	{
				cnt = 256;
				W25Q128_SectorErase(256);	
	}	
	//xTaskNotifyGive(CPU_task_Handler);//使用该函数即可打印CPC占用率
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    uartSemaphore = xSemaphoreCreateBinary(); //创建串口接收完成锁 
		uartQueue = xQueueCreate( 1, sizeof(char *) );//串口发送队列
		DHT11Queue = xQueueCreate(5,sizeof(u8) * 2);//温湿度数据队列
		xMyHandleTask1 = xTimerCreate("mytimer",5000,pdTRUE,NULL,MyTimerCallbackFunction);//创建自动加载软件定时器 5000Tick触发一次
	
    xTaskCreate((TaskFunction_t )ESP8266_task,     
                (const char*    )"esp8266Start_task",   
                (uint16_t       )100, 
                (void*          )NULL,
                (UBaseType_t    )3,
                (TaskHandle_t*  )&esp8266Task_Handler); 
	  xTaskCreate(Receive_Task,"Receive_Task",100,NULL,2,&Receive_Task_Handler);	
		xTaskCreate(led_task,"led_task",100,NULL,1,&led_task_Handler);	
		xTaskCreate(DHT11_task,"DHT11_task",100,NULL,1,&DHT11_task_Handler);
		xTaskCreate(ReadHistoricalData_task,"ReadHistoricalData_task",200,NULL,1,&ReadHistoricalData_task_Handler);
		xTaskCreate(ReadW25Q128_task,"ReadW25Q128_task",100,NULL,1,&ReadW25Q128_task_Handler);
		xTaskCreate(KEY_SuotPut_task,"KEY_SuotPut_task",100,NULL,1,&KEY_SuotPut_task_Handler);
		xTaskCreate(CPU_task,"CPU_task",500,NULL,1,&CPU_task_Handler);
		vTaskSuspend(DHT11_task_Handler);//临时睡眠等待唤醒		
		vTaskSuspend(ReadW25Q128_task_Handler);									
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

void ESP8266_task(void *pvParameters)
{
    while(1)
    {
			  debug_printf("任务一在运行中\r\n");	 
				ESP8266_Init(USART3, 115200);                      // 初始化;    参数:串口端口,波特率     注意:波特率默认为115200
				ESP8266_JoinAP(SSID, WIFIPassword);     // 加入WiFi;  参数: SSID, Password;      注意:ESP8266、手机所加入的Wifi是同一个AP(路由)
				ESP8266_TCPConnect(ServerIP, Port);          // 建立连接;  参数: 目标IP地址,端口号;  注意:这个IP和端口号,在目标APP“TCP连接”界面的正上方有显示
				ESP8266_EnablePassThrough();                      // 开始透明传输		
			  xReturn = xTaskNotifyGive(Receive_Task_Handler);
			  xReturn = xTaskNotifyGive(led_task_Handler);					
				if( xReturn == pdTRUE )
				{
						debug_printf("Receive1_Task_Handle 任务通知发送成功!\r\n");
						
				}	
				vTaskDelete(NULL); //删除开始任务			
		 }
}

void led_task(void *pvParameters)
{
		ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待任务通知
		xTimerStart(xMyHandleTask1, 0);//启动软件定时器
//		W25Q128_SectorErase(0x100);//清空w25q128第二页地址数据
    while(1)
    {
        LED1=0;
        vTaskDelay(500);
        LED1=1;
        vTaskDelay(500);
				//debug_printf("task tow is runing to\r\n");
    }
}
/*
		功能:读取温湿度数据把数据写入队列
*/
void DHT11_task(void *pvParameters)
{	 
    while(1)
    {	
				ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待按键任务锁
				DHT11_Red();				
				vTaskDelay(100);
		}
}
/*
		功能:读出按键按下缓存的数据		
*/

void ReadW25Q128_task(void *pvParameters)
{
		uint8_t ArrayRead[2];	
    while(1)
    {
				ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
				W25Q128_ReadData(0x000000, ArrayRead, 2);
				OLED_ShowNum(3, 3, ArrayRead[0], 2);
				OLED_ShowNum(3, 6, ArrayRead[1], 2);
				debug_printf("读出数据=%d %d\r\n",ArrayRead[0],ArrayRead[1]);	 
		}
}
/*
		功能:	当按下按键一的时候任务通知锁打开,通知唤醒温湿度读取任务,写入外部Flash
		通知唤醒读取外部Flash读取任务
*/

void KEY_SuotPut_task(void *pvParameters)
{
			uint8_t ArrayWrite[2];
			uint8_t temp=0,humi=0;
			char Dht11Dtat[10];
	
			while(1)
			{
					ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
					xTaskNotifyGive(DHT11_task_Handler);		
					vTaskResume(DHT11_task_Handler);
					xQueueReceive(DHT11Queue,&humi,portMAX_DELAY);
					xQueueReceive(DHT11Queue,&temp,portMAX_DELAY);
					ArrayWrite[0] = humi;
					ArrayWrite[1] = temp;					
					sprintf(Dht11Dtat, "humi=%d temp=%d\r\n", humi,temp);
					USART3_SendString(Dht11Dtat);	//向服务器发送一次数据
					OLED_ShowNum(2, 3, ArrayWrite[0], 2);
					OLED_ShowNum(2, 6, ArrayWrite[1], 2);	
					W25Q128_SectorErase(0x00000);//擦除扇区 
					W25Q128_PageProgram(0x00000, ArrayWrite, 2);  
					debug_printf("发送数据=%d %d\r\n",ArrayWrite[0],ArrayWrite[1]);	 
					xTaskNotifyGive(ReadW25Q128_task_Handler);	
					vTaskResume(ReadW25Q128_task_Handler);
			}
}
/*
		功能:按下KEY2按键读取历史数据,将外部Flash第二页一整页读取
*/

static int count=256;
void ReadHistoricalData_task(void *pvParameters)//读取队列中的数据
{
		uint8_t  HistoricalData[2],j;
		while(1)
		{
					ulTaskNotifyTake(pdTRUE,portMAX_DELAY);		
					for(j=0;j<127;j++)
					{
							W25Q128_ReadData(count, HistoricalData, 2);
							debug_printf("historical data humi=%d temp=%d\r\n",HistoricalData[0],HistoricalData[1]);	
							count+=2;
					}
					if(count>511)//防止读取外部Flash超过第三页
					{
								count = 256;
					}	
		}
}

//CPU任务函数
void CPU_task(void *pvParameters)
{
  uint8_t CPU_RunInfo[400];//保存任务运行时间信息
	
	while(1)
  {
		ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待按键任务锁
		memset(CPU_RunInfo,0,400);//信息缓冲区清零
		vTaskList((char *)&CPU_RunInfo);  //获取任务运行时间信息
		
		debug_printf("---------------------------------------------\r\n");
		debug_printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");
		debug_printf("%s", CPU_RunInfo);
		debug_printf("---------------------------------------------\r\n");

		memset(CPU_RunInfo,0,400);				//信息缓冲区清零

		vTaskGetRunTimeStats((char *)&CPU_RunInfo);

		debug_printf("任务名       运行计数         利用率\r\n");
		debug_printf("%s", CPU_RunInfo);
		debug_printf("---------------------------------------------\r\n\n");
   }
}


标签:STM32F103,ESP8266,FreeRTOS,void,0x00,uint8,OLED,GPIO,0x20
From: https://blog.csdn.net/m0_73397094/article/details/140781095

相关文章

  • FreeRtos笔记1
    记录学习过程:了解简单的Arm架构,CPU中各种寄存器的作用:堆的含义(就是空闲的内存),堆的作用是用来管理这些内存(堆函数,链表):内存的栈-->每个任务都有独属于自己的栈,在自己的任务栈中会保存函数,局部变量,还有自己的现场:任务是如何进行的:任务的调度过程:......
  • 手把手教你玩转ESP8266(原理+驱动)
    在嵌入式开发中,无线通信的方式有很多,其中WIFI是绕不开的话题。说到WIFI通信,就不得不提ESP8266了。ESP8266是一款高性能的WIFI串口模块,实现透明传输。只要有一定的串口知识,不需要知道WIFI原理就可以上手,在业内应用广泛。1.源码下载STM32F103C8T6模板工程链接:https......
  • FreeRTOS操作系统(详细速通篇)——— 第十五章(完结)
           本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。适用于快速了解FreeRTOS并进行开发、突击面试、对新手小白非常友好。期待您的后续关注和订阅!目录软件定时器和低功耗模式1软件定时器1.1什么是定时器?1.2软件定时器的优缺点1.3Free......
  • 02.FreeRTOS的移植
    文章目录FreeRTOS移植到STM32F103ZET6上的详细步骤1.移植前的准备工作2.添加FreeRTOS文件3.修改SYSTEM文件4.修改中断相关文件5.修改FreeRTOSConfig.h文件6.可选步骤FreeRTOS移植到STM32F103ZET6上的详细步骤1.移植前的准备工作**基础工程:**内存管理部分的......
  • 使用FreeRTOS官方移植,移植到STM32F1平台中
    本教程基本参照[野火]《FreeRTOS内核实现与应用开发实战指南》,本人相当推荐此教程,尤其是第一部分从0开始写内核,虽然比较晦涩,但是学完之后对FreeRTOS的运行原理还有框架的认识会有一个很大的提高。首先获取FreeRTOS的源码我们从官网下载9.0版本的压缩包解压后Plus中包含......
  • STM32F103 SPI详解及示例代码
    1SPI协议详解 SPI是串行外设接口(SerialPeripheralInterface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10......
  • 【嵌入式DIY实例-ESP8266篇】- LCD ST7789显示BME280传感器数据
    LCDST7789显示BME280传感器数据文章目录LCDST7789显示BME280传感器数据1、硬件准备2、代码实现本文将介绍如何使用ESP8266NodeMCU开发板(ESP12-E模块)和BME280气压、温度和湿度传感器构建一个简单的气象站。NodeMCU微控制器(ESP8266EX)从BME280......
  • STM32+ESP8266-连接阿里云-创建云产品流转实现STM32与Android app通讯(1)
    前言本文章的内容为STM32通过ESP8266利用AT指令连接阿里云平台,并创建设备和创建云产品流转主题,来为实现Androidapp与STM32的发送接收数据做准备。Androidapp的实现由于篇幅不宜过长,将放到下一篇文章中。演示视频实现一个简单的app来控制stm32开关灯、蜂鸣器、门(舵机),显示温......
  • 零基础STM32单片机编程入门(二十二) ESP8266 WIFI模块实战含源码
    文章目录一.概要二.ESP8266WIFI模块主要性能参数三.ESP8266WIFI模块芯片内部框图四.ESP8266WIFI模块原理图五.ESP8266WIFI模块与单片机通讯方法1.硬件连接2.ESP8266模块AT指令介绍六.STM32单片机与ESP8266WIFI模块通讯实验1.硬件准备2.软件工程3.软件主要代码4.实验......
  • FreeRTOS操作系统(详细速通篇)——— 第十章
            本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。适用于快速了解FreeRTOS并进行开发、突击面试、对新手小白非常友好。期待您的后续关注和订阅!目录FreeRTOS任务相关API函数1任务优先级相关API函数(1)uxTaskPriorityGet(2)vTaskPriorityS......