常用API
esp_get_free_heap_size()//获取空闲堆内存
任务状态
运行:当任务实际执行时,它被称为处于运行状态。任务当前正在使用处理器;
就绪:能够执行但目前未执行的任务,因为同等或更高优先级的不同任务正在执行;
阻塞:正在等待时间或外部事件。例如一个任务调用vTaskDelay(),它将被阻塞;
挂起:与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。 相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态
低优先级数字表示低优先级任务。 空闲任务的优先级为零 (tskIDLE_PRIORITY)。被置于运行状态的任务 始终是可运行的最高优先级任务。
FreeRTOS 默认使用固定优先级的抢占式 调度策略,对同等优先级的任务执行时间片轮询调度:
1.Task相关
1.系统启动流程
- 各函数调用关系
freeRTOS相关
app_main -> main_task -> esp_startup_start_app_common -> esp_startup_start_app ->
(esp_system文件中)
start_cpu0_default -> start_cpu0(硬件相关的初始化) -> g_startup_fn(可调用不同CPU初始化程序) -> SYS_STARTUP_FN()(宏定义) -> call_start_cpu0(或者其他内核) -> /* Default entry point: */ENTRY(call_start_cpu0);(esp32设计的入口,对硬件初始化,创建主task,进行任务调度)
2.创建 删除任务
创建任务
task. h
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, //指向该任务函数的指针
const char * const pcName, //函数名,字符串格式
configSTACK_DEPTH_TYPE usStackDepth, //堆栈可以容纳的字数
void *pvParameters, //传入该任务函数的形参,没有则为NULL
UBaseType_t uxPriority, //该任务优先级,数字越大优先级越高,0为空闲任务,最高( 25 )
TaskHandle_t *pxCreatedTask //该任务函数的句柄,可用以后续删除任务等操作
);
//example
//********创建任务
void myTask(void *pvParam)
{
while (1){
printf("hello");
vTaskDelay(10000 / portTICK_RATE_MS);
}
}
void app_main(void *param){
xTaskCreate(myTask, "myTask", 1024, NULL, 1, NULL); //创建任务
}
//*******删除任务法1:利用句柄
void app_main(void *param){
TaskHandle_t myTaskHandle=NULL;
xTaskCreate(myTask, "myTask", 1024, NULL, 1, &myTaskHandle); //创建任务
vTaskDelay(10000 / portTICK_RATE_MS); //延时一秒再删除,否则刚创建就被删除,无现象。
if(myTaskHandle != NULL){
vTaskDelete(myTaskHandle);
}
//********创建任务
//*******删除任务法2:在任务函数里自己删除自己
void myTask(void *pvParam)
{
while (1){
printf("hello");
vTaskDelay(10000 / portTICK_RATE_MS);
vTaskDelete(NULL); //创建任务时句柄形参为NULL
}
void app_main(void *param){
xTaskCreate(myTask, "myTask", 1024, NULL, 1, NULL); //创建任务
}
3.Task参数
//将结构体类型的变量传递给Task
typydef struct A_STRUCT
{
int i=1;
int j=1;
}xStruct;
xStruct xStrTest={6,9};
void myTask(void *pvParam)
{
xStruct *pStrTest;
pStrTest=(xStruct *)pvParam;
printf("i=%d\n",pStrTest->i);
printf("j=%d\n",pStrTest->j);
vTaskDelay(10000 / portTICK_RATE_MS);
vTaskDelete(NULL); //创建任务时句柄形参为NULL
}
void app_main(void *param){
xTaskCreate(myTask, "myTask", 1024,(void *)&xStrTest, 1, NULL); //创建任务
}
4.Task优先级别
优先级别:0-25,数字越大级别越高;
- 取得优先级别的接口:UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );
void myTask(void *pvParam)
{
xStruct *pStrTest;
pStrTest=(xStruct *)pvParam;
printf("i=%d\n",pStrTest->i);
printf("j=%d\n",pStrTest->j);
vTaskDelay(10000 / portTICK_RATE_MS);
vTaskDelete(NULL); //创建任务时句柄形参为NULL
}
void app_main(void *param){
UBaseType_t iPriority=0;
TaskHandle_t pxTask = NULL;
xTaskCreate(myTask, "myTask", 1024,(void *)&xStrTest, 24, NULL); //创建任务
iPriority = uxTaskPriorityGet(pxTask);
}
-
Same Priority
创建任务后,顺序运行,切换相同的时间片 -
设置优先级别的接口:void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority );
5.Task挂起和恢复
可任务内部挂起(任务句柄为NULL)或外部挂起;
void vTaskSuspend( TaskHandle_t pxTaskToSuspend );
void vTaskResume( TaskHandle_t pxTaskToResume );
void vTaskSuspendAll( void ); //只运行当前任务,调度器被挂起,freeRTOS API都不可被调用
BaseType_t xTaskResumeAll( void );
void vTaskList( char *pcWriteBuffer ); // 打印所有任务的情况(状态 堆栈 优先级……)//可能已经被弃用了。。
6.Task堆栈
//用以确定某任务剩余的堆栈,返回值越接近0则任务越接近溢出,以确定创建任务时合适的堆栈
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
- 单位换算:
1B(字节)=8b(位)
1 KB = 1024 B
1 MB = 1024 KB
1 GB = 1024 MB
1TB = 1024GB
7.Task看门狗
- 以下task一卡直在while(1)的死循环里,导致无法执行空闲任务idle1,会触发Task看门狗
void myTask(void *pvParam)
{
while(1)
{
;
//printf("task1");
//vTaskDelay(10000 / portTICK_RATE_MS);
}
}
void app_main(void *param){
xTaskCreate(myTask, "myTask", 1024,NULL,1, NULL); //创建任务
}
- 解决措施:
- 加阻塞函数例如延时,可阻塞当前任务的运行,使得调度器可以调度低优先级的任务,可以去喂狗;
- 将优先级改为0 ,该任务和空闲任务有相同的时间片,空闲任务得以运行。
2.Queue相关
//头文件
#include "FreeRTOS.h"
#include “queue.h”
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength, //队列长度
UBaseType_t uxItemSize );
//发数据到队列
BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
- 多入单出:
Queue Set
问题:Queue Set里各队列数据类型不一致如何处理?
Queue Mailbox
- 单入多出
Software Timer
- Software Timer与硬件、平台无关,均使用相同的命定调用软件定时器
- Software Timer和硬件定时器数量有限制不同,可以通过配置很多个~
#include “FreeRTOS.h”
#include “timers.h”
//创建定时器
TimerHandle_t xTimerCreate( const char *pcTimerName, //定时器名称
const TickType_t xTimerPeriod, //周期
const UBaseType_t uxAutoReload, //是否多次执行
void * const pvTimerID, //ID
TimerCallbackFunction_t pxCallbackFunction ) //回调函数
//启动定时器
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
//停止定时器
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
//获取定时器name
const char * pcTimerGetName( TimerHandle_t xTimer );
//获取定时器ID
void *pvTimerGetTimerID( TimerHandle_t xTimer );
信号量
就像信号灯控制汽车一样,信号量可以控制任务。当没有信号量时,两个优先级相同的任务有相同的时间片,轮番执行;而加入信号量控制,可以使得第一个任务执行完之后再执行第二个任务。
#include “FreeRTOS.h”
#include “semphr.h”
二进制信号量
//1.创建信号量
SemaphoreHandle_t xSemaphoreCreateBinary( void );
//2.释放信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
//以下在任务函数中
//3.获取信号量
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait )
//4.释放信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
计数型信号量
//1.创建信号量
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
//2.释放信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
//以下在任务函数中
//3.返回信号量的计数
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
//4.获取信号量
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait )(信号量--)
//5.释放信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );(信号量++)
//example——main.c
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/semphr.h"
SemaphoreHandle_t xSemaphoreCountHandle;
void taskCarin(void *pvParam)
{
int emptyCou=0;
BaseType_t result;
while(1)
{
emptyCou = uxSemaphoreGetCount(xSemaphoreCountHandle);
printf("emptyCou:%d\n",emptyCou);
result = xSemaphoreTake(xSemaphoreCountHandle, 0);
if (result == pdPASS)
{
printf("one car in\n");
}
else
{
printf("no space\n");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void taskCarout(void *pvParam)
{
while (1)
{
vTaskDelay(pdMS_TO_TICKS(6000));
xSemaphoreGive( xSemaphoreCountHandle);
printf("one car out\n");
}
}
void app_main(void )
{
xSemaphoreCountHandle = xSemaphoreCreateCounting(5, 5);
// xSemaphoreGive( xSemaphoreCountHandle);
xTaskCreate(taskCarin,"taskCarin",5*1024,NULL,1,NULL);
xTaskCreate(taskCarout,"taskCarout",5*1024,NULL,1,NULL);
}
Mutex 互斥量
从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放锁。 一个线程占有一个互斥量以后,不允许其他线程继续占有这个互斥量。 其他试图访问这个互斥量的线程会被阻塞住。 直到占有互斥量的线程主动解锁。
递归互斥量
事件组
#include “FreeRTOS.h”
#include “event_groups.h”
//main 中创建group
EventGroupHandle_t xEventGroupCreate( void );
//设置对应位的值
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet )
//task1等待对应位的设置后再解除阻塞
EventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAll)
//task2中设置对应位后task1就解除阻塞
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
xEventGroupWaitBits事件组等待 和xEventGroupSync时间组同步的区别
- 事件组等待
task2 3在设置后能继续运行
- 事件组同步
会等待需要同步的任务设置后再一起运行
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );
消息通知
可以控制任务阻塞与解除阻塞。
/* Prototypes of the two tasks created by main(). */
static void prvTask1( void *pvParameters );
static void prvTask2( void *pvParameters );
/* Handles for the tasks create by main(). */
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
/* Create two tasks that send notifications back and forth to each other, then
start the RTOS scheduler. */
void main( void )
{
xTaskCreate( prvTask1, "Task1", 200, NULL, tskIDLE_PRIORITY, &xTask1 );
xTaskCreate( prvTask2, "Task2", 200, NULL, tskIDLE_PRIORITY, &xTask2 );
vTaskStartScheduler();
}
/*-----------------------------------------------------------*/
static void prvTask1( void *pvParameters )
{
for( ;; )
{
/* Send a notification to prvTask2(), bringing it out of the Blocked
state. */
xTaskNotifyGive( xTask2 ); //通知task2可以解除阻塞了
}
}
/*-----------------------------------------------------------*/
static void prvTask2( void *pvParameters )
{
for( ;; )
{
/* Block to wait for prvTask1() to notify this task. */
ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); //阻塞,等待通知后再接触阻塞
}
}
//根据每一位的设定值处理不同的工作
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait );
//例如
void vAnEventProcessingTask( void *pvParameters )
{
uint32_t ulNotifiedValue;
for( ;; )
{
xTaskNotifyWait( 0x00, /* Don't clear any notification bits on entry. */
ULONG_MAX, /* Reset the notification value to 0 on exit. */
&ulNotifiedValue, /* Notified value pass out in ulNotifiedValue. */
portMAX_DELAY ); /* Block indefinitely. */
/* Process any events that have been latched in the notified value. */
if( ( ulNotifiedValue & 0x01 ) != 0 )
{
/* Bit 0 was set - process whichever event is represented by bit 0. */
prvProcessBit0Event();
}
if( ( ulNotifiedValue & 0x02 ) != 0 )
{
/* Bit 1 was set - process whichever event is represented by bit 1. */
prvProcessBit1Event();
}
if( ( ulNotifiedValue & 0x04 ) != 0 )
{
/* Bit 2 was set - process whichever event is represented by bit 2. */
prvProcessBit2Event();
}
/* Etc. */
}
}
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
/* Set the notification value of the task referenced by xTask3Handle to 0x50,
even if the task had not read its previous notification value. */
//参数eAction 一般取 - eSetBits –,例如:
/* Set bit 8 in the notification value of the task referenced by xTask1Handle. */
xTaskNotify( xTask1Handle, ( 1UL << 8UL ), eSetBits );
流数据StreamBuffer
#include “FreeRTOS.h”
#include “stream_buffer.h”
\\创建数据流
\\xTriggerLevelBytes表示在达到这么多个数据前一直会阻塞
StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes,size_t xTriggerLevelBytes )
\\task1里发送数据
size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
TickType_t xTicksToWait )
\\task2里接收数据
size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
void *pvRxData,
size_t xBufferLengthBytes,
TickType_t xTicksToWait );
确定流数据缓冲区的大小
xStreamBufferSpacesAvailable() //获取某个流数据剩余可用空间的大小,以便合理设置流数据的大小
Message Buffer消息缓冲区
与流缓冲区的区别:
- 区别1
消息缓冲区:可以从缓冲区取出一条完整的数据;
流数据缓冲区:只要流缓冲区中有足够的数据,接收缓冲区足够大,会把缓冲区中的所有数据取出来 - 区别2
当接收buffer的大小小于一条meaasge的大小时,
流数据缓冲区:能接收设定大小的数据,直至接收完成。
消息缓冲区:不能接收数据,接收的内容为空,返回为0。
JTAG
或者把Adapter集成到芯片
open ocd最初源于05年的毕业论文,设计思想如下:
WIFI
- 三层架构及函数关系
TCP客户端
ESP32 nvs flash
操作步骤:仅记录核心代码
- 初始化
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK_WITHOUT_ABORT(ret);
- 存
//打开nvs,注意检测是否打开成功
nvs_handle_t my_handle;
err = nvs_open(ROM_NV, NVS_READWRITE, &my_handle); //可读可写
if (err != ESP_OK) {
gin_printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
return RET_0XFFFFFFFF ;
}
else
{
//...成功打开后的操作
err = nvs_set_u32(my_handle,(const char *)nv_name, *data_type); //选择适合的set函数
//set后一定要commit
if (nvs_commit(my_handle) != ESP_OK)
{
gin_printf("nvs_commit err\r\n");
}
//关闭
nvs_close(my_handle);
}
- 读取
esp_err_t err = -1;
nvs_handle_t my_handle;
char nv_name[64] = {0} ;
err = nvs_open(ROM_NV, NVS_READONLY, &my_handle); //只读
if (err != ESP_OK) {
gin_printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
return RET_0XFFFFFFFF ;
}
else
{
err = nvs_get_u32(my_handle, (const char *)nv_name, read_data);
nvs_close(my_handle);
}
注意:set get有多种类型可选,其中
esp_err_t nvs_set_str(nvs_handle_t c_handle, const char* key, const char* value)//用以读写字符串
esp_err_t nvs_set_blob(nvs_handle_t c_handle, const char* key, const void* value, size_t length)//用以读写结构体 链表类的
HTTP(Hypertext Transfer Protocol)
运行在TCP之上,应用层
- 请求数据包
method:get,post,put - 应答数据包
status code:200,301,404 - HTTP URL(uniform resource locator)解释
webSocket
弊端当需要大量数据的时候,客户端需要发大量请求给服务器,会造成资源浪费。、
可节省服务端资源,节省带宽资源,提高沟通效率
- 数据帧组成
详细内容参考官网https://www.rfc-editor.org/rfc/rfc6455.html#section-5.2
- 举例:一个图片分3帧发送
WebSocket Server
包含三个部分:
- Shake hand;
- Receive data;
- Send data