前言
本人初学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