当前位置: 首页 > news >正文

任务通知的本质(任务通知车辆运行) 软件定时器的本质(增加游戏音效)

 任务通知的本质

没有任务通知

        所谓"任务通知",你可以反过来读"通知任务"。
        我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可
以明确指定:通知哪个任务。
       使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构
体通信

任务通知的存在 

使用任务通知时,任务结构体 TCB 中就包含了内部对象,可以直接接收别人发过来的 "
通知 "

任务通知的特性

优势及限制

任务通知的优势:
任务通知的优势:
效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队
列、信号量、事件组都有大的优势。
更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无
需额外创建结构体
任务通知的限制: 
不能发送数据给 ISR
ISR 并没有任务结构体,所以无法使用任务通知的功能给 ISR 发送数据。但是
ISR 可以使用任务通知的功能,发数据给任务。
数据只能给该任务独享
使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、 ISR 都可
以访问这些数据。使用任务通知时,数据存放入目标任务中,只有它可以访问这
些数据。
在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某
个任务,而不是把一个数据源的数据发给多个任务。
无法缓冲数据
使用队列时,假设队列深度为 N ,那么它可以保持 N 个数据。
使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。
无法广播给多个任务
使用事件组可以同时给多个任务发送事件。
使用任务通知,只能发个一个任务。
如果发送受阻,发送方无法进入阻塞状态等待
假设队列已经满了,使用 xQueueSendToBack() 给队列发送数据时,任务可以进
入阻塞状态等待发送完成。
使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返
回错误。

通知状态和通知值

每个任务都有一个结构体:TCB(Task Control Block),里面有 2 个成员:
一个是 uint8_t 类型,用来表示通知状态
一个是 uint32_t 类型,用来表示通知值
typedef struct tskTaskControlBlock
{volatile StackType_t	*pxTopOfStack;	/*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */#if ( portUSING_MPU_WRAPPERS == 1 )xMPU_SETTINGS	xMPUSettings;		/*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */#endifListItem_t			xStateListItem;	/*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ListItem_t			xEventListItem;		/*< Used to reference a task from an event list. */UBaseType_t			uxPriority;			/*< The priority of the task.  0 is the lowest priority. */StackType_t			*pxStack;			/*< Points to the start of the stack. */char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )StackType_t		*pxEndOfStack;		/*< Points to the highest valid address for the stack. */#endif#if ( portCRITICAL_NESTING_IN_TCB == 1 )UBaseType_t		uxCriticalNesting;	/*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */#endif#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t		uxTCBNumber;		/*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */UBaseType_t		uxTaskNumber;		/*< Stores a number specifically for use by third party trace code. */#endif#if ( configUSE_MUTEXES == 1 )UBaseType_t		uxBasePriority;		/*< The priority last assigned to the task - used by the priority inheritance mechanism. */UBaseType_t		uxMutexesHeld;#endif#if ( configUSE_APPLICATION_TASK_TAG == 1 )TaskHookFunction_t pxTaskTag;#endif#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )void			*pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];#endif#if( configGENERATE_RUN_TIME_STATS == 1 )uint32_t		ulRunTimeCounter;	/*< Stores the amount of time the task has spent in the Running state. */#endif#if ( configUSE_NEWLIB_REENTRANT == 1 )/* Allocate a Newlib reent structure that is specific to this task.Note Newlib support has been included by popular demand, but is notused by the FreeRTOS maintainers themselves.  FreeRTOS is notresponsible for resulting newlib operation.  User must be familiar withnewlib and must provide system-wide implementations of the necessarystubs. Be warned that (at the time of writing) the current newlib designimplements a system-wide malloc() that must be provided with locks. */struct	_reent xNewLib_reent;#endif#if( configUSE_TASK_NOTIFICATIONS == 1 )volatile uint32_t ulNotifiedValue;volatile uint8_t ucNotifyState;#endif/* See the comments above the definition oftskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 Macro has been consolidated for readability reasons. */uint8_t	ucStaticallyAllocated; 		/*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */#endif#if( INCLUDE_xTaskAbortDelay == 1 )uint8_t ucDelayAborted;#endif} tskTCB;
通知状态有 3 种取值:
taskNOT_WAITING_NOTIFICATION :任务没有在等待通知
taskWAITING_NOTIFICATION :任务在等待通知
taskNOTIFICATION_RECEIVED :任务接收到了通知,也被称为 pending(
数据了,待处理 )
##define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /*
也是初始状态 */
##define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 )
##define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 )

任务通知的使用

两类函数

任务通知有 2 套函数,简化版、专业版,列表如下:
简化版函数的使用比较简单,它实际上也是使用专业版函数实现的
⚫ 专业版函数支持很多参数,可以实现很多功能

 xTaskNotifyGive

xTaskNotifyGive 是 FreeRTOS 提供的一个用于给指定任务发送通知的函数。它是一个简化的接口,用于将目标任务的通知值增加 1,相当于向该任务发送一个信号。它通常用于同步和信号传递,在某些场景下,可以替代信号量(Semaphore)或消息队列(Queue)等机制。

函数原型
BaseType_t xTaskNotifyGive(TaskHandle_t xTask);
参数说明
  • xTask: 目标任务的句柄,表示你要通知的任务。

    • 这个任务句柄可以通过 xTaskCreate 创建任务时返回的任务句柄获得。
    • 如果传入 NULL,则会通知当前任务自己。
函数说明

xTaskNotifyGive 会将指定任务的通知值增加 1。通知值是一个 32 位的无符号整数,这个整数在任务间通信时扮演着标志或信号的角色。当任务通过 xTaskNotifyWait 等函数接收通知时,它可以检查或处理这个通知值。

返回值
  • pdPASS:表示通知成功发送。
  • pdFAIL:如果通知未成功发送(例如,目标任务不存在),则返回 pdFAIL
使用场景

xTaskNotifyGive 主要用于以下场景:

  • 任务间同步:一个任务通知另一个任务,表示它已经完成了某个操作,或者发生了某个事件,通知目标任务继续执行。
  • 信号传递:用于任务之间传递简单的标志或信号。例如,任务 A 完成某项工作后通知任务 B 执行下一步操作。

ulTaskNotifyTake

ulTaskNotifyTake 是 FreeRTOS 提供的一个函数,用于接收任务通知,它是任务通知机制的一部分。与 xTaskNotifyGive 配合使用,ulTaskNotifyTake 允许一个任务等待并响应来自其他任务的通知。通常,用于任务间同步、信号传递等场景。

函数原型
 
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait);
参数说明
  • xClearCountOnExit:

    • 这个参数决定了在调用 ulTaskNotifyTake 时是否要清除任务的通知计数器。
    • 如果设置为 pdTRUE,任务通知计数器会在退出时清零,表示已成功处理该通知。
    • 如果设置为 pdFALSE,则计数器不会清除,这样任务可以在后续的调用中继续处理通知(类似于一个“计数器”)。
  • xTicksToWait:

    • 这个参数指定任务等待通知的最长时间(以节拍 tick 为单位)。
    • 如果设置为 portMAX_DELAY,则任务会一直等待通知,直到收到通知为止。如果设置为 0,则任务会立即返回,不会等待。
返回值
  • 0: 如果任务没有接收到通知,或者接收到的通知计数器已经被清除。
  • >0: 返回接收到的通知计数器的值(通常是任务通知的增量)。如果 xClearCountOnExit 为 pdFALSE,那么返回值表示当前的通知计数器值。如果为 pdTRUE,则会清除计数器,返回接收到的通知值。
函数说明

ulTaskNotifyTake 用于接收一个任务的通知,它可以等待指定的时间。如果在该时间内没有接收到通知,任务将返回 0。如果在指定时间内接收到通知,返回接收到的通知值(通常是 1)。

  • 任务在接收到通知后,可以执行相应的操作。
  • 如果通知的计数器被清除,则该任务的通知值将被重置为 0(除非 xClearCountOnExit 为 pdFALSE)。
使用场景

ulTaskNotifyTake 通常用于以下场景:

  • 任务间同步:一个任务等待另一个任务的通知(例如,当任务 A 完成工作时,通知任务 B 执行后续操作)。
  • 信号传递:通过任务通知,任务 A 可以通知任务 B 执行某些操作。

xTaskNotify

xTaskNotify 是 FreeRTOS 提供的一个函数,允许任务通过任务通知机制与其他任务进行通信或同步。与 ulTaskNotifyTakexTaskNotifyGive 函数配合使用,xTaskNotify 提供了更灵活的任务通知机制,它允许任务发送或接收通知,同时支持不同的通知方式和通知值。

函数原型
BaseType_t xTaskNotify(TaskHandle_t xTask, uint32_t ulValue, eNotifyAction eAction);
参数说明
  • xTask:

    • 目标任务的句柄,指明哪个任务会接收这个通知。如果任务句柄为 NULL,则表示将通知调用此函数的任务。
  • ulValue:

    • 发送的通知值。该值可以是任何 32 位的整数,通常用于携带一些信息或者状态。
  • eAction:

    • 通知的操作类型。这个参数使用枚举类型 eNotifyAction,它定义了任务通知的行为。主要有以下几种常见的操作:
      • eSetBits: 设置通知位。将通知值中的指定位设置为 1
      • eIncrement: 增加通知值(递增通知值的计数)。
      • eSetValueWithOverwrite: 设置通知值,如果原值存在,则覆盖。
      • eSetValueWithoutOverwrite: 设置通知值,如果原值已经存在,则不覆盖。
      • eClearBits: 清除通知位。将通知值中的指定位设置为 0
返回值
  • pdPASS: 如果通知成功发送或操作完成。
  • errTASK_NOT_FOUND: 如果目标任务无效(例如 xTask 为无效的任务句柄)。
通知机制

xTaskNotify 是一种轻量级的任务间通知机制,通过修改任务的通知值来进行信号传递。它与传统的信号量、队列和事件组相比,具有更低的开销和更快的响应速度。

xTaskNotifyWait

xTaskNotifyWait 是 FreeRTOS 提供的一个函数,用于任务等待另一个任务的通知。与 ulTaskNotifyTake 类似,它可以用于同步任务之间的事件或信号。xTaskNotifyWait 可以让一个任务等待某些通知位被设置或某个通知值的变化,直到发生指定的事件或者超时。

函数原型
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToSetOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );
参数说明
  • ulBitsToClearOnEntry:

    • 当任务等待通知时,清除通知值中某些位。即使在任务等待期间这些位被设置,函数返回时也会把这些位清除。
  • ulBitsToSetOnExit:

    • 当任务成功接收到通知后,设置通知值中的某些位。这些位将在通知发生时被设置。
  • pulNotificationValue:

    • 指向一个 uint32_t 变量的指针,用于接收任务的通知值。若没有兴趣获取通知值,则可以传入 NULL
  • xTicksToWait:

    • 任务等待通知的时间(以 ticks 为单位)。如果在指定的时间内没有接收到通知,则返回 pdFALSE
返回值
  • pdPASS: 表示通知已被成功接收。
  • pdFAIL: 表示未能接收到通知,可能是因为超时或者其他原因。
典型用途

xTaskNotifyWait 常用于任务在执行之前等待其他任务的通知,或在处理某些操作之前确保任务之间的同步。

使用场景
  1. 等待某个通知: 任务等待另一个任务设置某个通知位或通知值。
  2. 设置和清除通知位: 在任务间传递信号并对通知位进行清除或设置。
  3. 等待超时机制: 设置等待超时时间,防止任务一直阻塞等待。

A通知B,B不接收A的通知

A通知B,B接收A的通知

任务通知车辆运行代码

 

/** Project: N|Watch* Author: Zak Kemble, contact@zakkemble.co.uk* Copyright: (C) 2013 by Zak Kemble* License: GNU GPL v3 (see License.txt)* Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>#include "cmsis_os.h"
#include "FreeRTOS.h"                   // ARM.FreeRTOS::RTOS:Core
#include "task.h"                       // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h"               // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h"                     // ARM.FreeRTOS::RTOS:Core#include "draw.h"
#include "resources.h"#include "driver_lcd.h"
#include "driver_ir_receiver.h"
#include "driver_rotary_encoder.h"
#include "driver_mpu6050.h"#define NOINVERT	false
#define INVERT		true#define CAR_COUNT	3
#define CAR_WIDTH	12
#define CAR_LENGTH	15
#define ROAD_SPEED	6static SemaphoreHandle_t g_xSemTicks; 
static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
static EventGroupHandle_t g_xEventCar;static TaskHandle_t g_TaskHandleCar2;
static TaskHandle_t g_TaskHandleCar3;struct car {int x;int y;int control_key;
};struct car g_cars[3] = {{0, 0, IR_KEY_1},{0, 17, IR_KEY_2},{0, 34, IR_KEY_3},
};static const byte carImg[] ={0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0,0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F,
};static const byte clearImg[30] ={0};static const byte roadMarking[] ={0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
};#if 0
void car_test(void)
{g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();draw_bitmap(0, 0, carImg, 15, 16, NOINVERT, 0);draw_flushArea(0, 0, 15, 16);draw_bitmap(0, 16, roadMarking, 8, 1, NOINVERT, 0);draw_flushArea(0, 16, 8, 1);while (1);
}
#endifstatic void ShowCar(struct car *pcar)
{draw_bitmap(pcar->x, pcar->y, carImg, 15, 16, NOINVERT, 0);draw_flushArea(pcar->x, pcar->y, 15, 16);
}static void HideCar(struct car *pcar)
{draw_bitmap(pcar->x, pcar->y, clearImg, 15, 16, NOINVERT, 0);draw_flushArea(pcar->x, pcar->y, 15, 16);
}static void Car1Task(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);/* 获得信号量 *///xSemaphoreTake(g_xSemTicks, portMAX_DELAY);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar); /* 调整位置 */pcar->x += 1;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);vTaskDelay(50);if (pcar->x == g_xres - CAR_LENGTH){/* 释放信号量 *///xSemaphoreGive(g_xSemTicks);/* 设置事件组: bit0 *///xEventGroupSetBits(g_xEventCar, (1<<0));/* 发出任务通知给car2,car3 */xTaskNotifyGive(g_TaskHandleCar2);xTaskNotify(g_TaskHandleCar3, 100, eSetValueWithOverwrite);vTaskDelete(NULL);}}}}
}static void Car2Task(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);/* 获得信号量 *///xSemaphoreTake(g_xSemTicks, portMAX_DELAY);/* 等待事件:bit0 *///xEventGroupWaitBits(g_xEventCar, (1<<0), pdTRUE, pdFALSE, portMAX_DELAY);ulTaskNotifyTake(pdTRUE, portMAX_DELAY);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x += 1;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);vTaskDelay(100);//mdelay(50);if (pcar->x == g_xres - CAR_LENGTH){/* 释放信号量 *///xSemaphoreGive(g_xSemTicks);/* 设置事件组: bit1 *///xEventGroupSetBits(g_xEventCar, (1<<1));vTaskDelete(NULL);}}}}
}static void Car3Task(void *params)
{struct car *pcar = params;struct ir_data idata;uint32_t val;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);/* 获得信号量 *///xSemaphoreTake(g_xSemTicks, portMAX_DELAY);/* 等待事件:bit0 and bit1 *///xEventGroupWaitBits(g_xEventCar, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);do {xTaskNotifyWait(~0, ~0, &val, portMAX_DELAY);} while (val != 100);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x += 1;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);//vTaskDelay(50);mdelay(50);if (pcar->x == g_xres - CAR_LENGTH){/* 释放信号量 *///xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}void car_game(void)
{int x;int i, j;g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();//g_xSemTicks = xSemaphoreCreateCounting(1, 1);//g_xSemTicks = xSemaphoreCreateMutex();g_xEventCar = xEventGroupCreate();/* 画出路标 */for (i = 0; i < 3; i++){for (j = 0; j < 8; j++){draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);draw_flushArea(16*j, 16+17*i, 8, 1);}}/* 创建3个汽车任务 */
#if 0	for (i = 0; i < 3; i++){draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);}
#endifxTaskCreate(Car1Task, "car1", 128, &g_cars[0], osPriorityNormal, NULL);xTaskCreate(Car2Task, "car2", 128, &g_cars[1], osPriorityNormal+2, &g_TaskHandleCar2);xTaskCreate(Car3Task, "car3", 128, &g_cars[2], osPriorityNormal+2, &g_TaskHandleCar3);	
}

 软件定时器的本质

软件定时器就是"闹钟",你可以设置闹钟,
30 分钟后让你起床工作
每隔 1 小时让你例行检查机器运行情况
软件定时器也可以完成两类事情:
" 未来 " 某个时间点,运行函数
周期性地运行函数
使用定时器跟使用手机闹钟是类似的:
指定时间:启动定时器和运行回调函数,两者的间隔被称为定时器的周期
(period)
指定类型,定时器有两种类型:
一次性 (One-shot timers)
这类定时器启动后,它的回调函数只会被调用一次;
可以手工再次启动它,但是不会自动启动它。
自动加载定时器 (Auto-reload timers )
这类定时器启动后,时间到之后它会自动启动它;
这使得回调函数被周期性地调用。
指定要做什么事,就是指定回调函数
实际的闹钟分为:有效、无效两类。软件定时器也是类似的,它由两种状态:
运行 (Running Active) :运行态的定时器,当指定时间到达之后,它的回调
函数会被调用
冬眠 (Dormant) :冬眠态的定时器还可以通过句柄来访问它,但是它不再运
行,它的回调函数不会被调用

软件定时器的运行依赖于硬件定时器的运行 

 软件定时器的函数

根据定时器的状态转换图,就可以知道所涉及的函数:
 一次性定时器

 一次性定时器(即 单次定时器),定时器在执行完一次周期后会自动停止

在 FreeRTOS 中,一次性定时器是通过 xTimerCreate() 函数创建的定时器,并且在创建时设置其为一次性定时器(pdFALSExAutoReload 参数)。一旦定时器到达设定的时间周期,它会执行指定的回调函数,并且在回调函数执行完毕后定时器会停止,并且不能再自动重复。

具体流程:
  1. 定时器启动后,会在设定的周期时间到达时触发定时器回调。
  2. 如果定时器是一次性定时器,回调函数执行完毕后,定时器会被自动停止,并且不会再被触发。
  3. 如果你希望定时器再次触发,需要重新创建和启动定时器。
一次性定时器的生命周期:
  • 定时器启动后,周期到期触发回调函数。
  • 回调函数执行完毕后,定时器会停止,并且不再自动重新启动。

如果想要定时器再次触发,你需要重新创建并启动一个新的定时器。

重要提示:
  • 如果你需要定时器在回调函数执行后仍然能够重新触发,需要将 xAutoReload 设置为 pdTRUE,即创建一个周期性的定时器(重复定时器)。
  • 一次性定时器的主要用途是执行一次性操作,例如延时任务、超时控制等,适用于只需要在某个时间点触发的任务。
创建

xTimerCreate 是 FreeRTOS 中创建定时器的函数。通过该函数,你可以创建一个新的定时器,并且配置定时器的一些属性,包括定时周期、自动重载、定时器标识符和回调函数等

函数原型
TimerHandle_t xTimerCreate(const char * const pcTimerName,           // 定时器的名称(字符串)const TickType_t xTimerPeriodInTicks,     // 定时器的周期(单位:ticks)const UBaseType_t uxAutoReload,           // 是否自动重载(0:不重载,1:自动重载)void * const pvTimerID,                   // 定时器标识符(可以是任何指针类型的数据)TimerCallbackFunction_t pxCallbackFunction // 定时器到期时执行的回调函数
);
参数说明
  1. pcTimerName:

    • 定时器的名称,类型为 const char *,这是一个可选的字符串参数,表示定时器的名称。
    • 如果不需要定时器名称,可以传入 NULL
  2. xTimerPeriodInTicks:

    • 定时器周期,以 FreeRTOS 系统 ticks 为单位。TickType_t 是一个系统特定的数据类型,表示系统时钟的刻度。
    • 例如,如果你的系统时钟频率为 1000Hz(即 1ms 每 tick),并且你希望定时器的周期为 1秒,那么 xTimerPeriodInTicks 应该是 1000。
  3. uxAutoReload:

    • 定时器是否自动重载的标志。uxAutoReload 为 pdTRUE 时,定时器会在触发后自动重新开始计时;为 pdFALSE 时,定时器只会触发一次,不会自动重载。
    • 设置为 pdFALSE 创建的是一次性定时器,即定时器到期后会停止,不会再次触发。
    • 设置为 pdTRUE 创建的是周期性定时器,即定时器到期后会自动重新开始计时。
  4. pvTimerID:

    • 这是一个指向数据的指针,用来为定时器关联用户定义的数据。你可以使用它来存储与定时器相关的任何信息或状态(例如计数器、标志等)。
    • 你可以将 NULL 传递给它,如果不需要存储额外数据。
  5. pxCallbackFunction:

    • 当定时器到期时执行的回调函数。该回调函数必须符合 TimerCallbackFunction_t 类型的定义。
    • 该回调函数的参数为 TimerHandle_t 类型的定时器句柄,通常通过该句柄可以获取更多关于定时器的信息。
返回值
  • 如果定时器创建成功,返回一个有效的定时器句柄(TimerHandle_t 类型)。
  • 如果定时器创建失败,返回 NULL
删除

xTimerDelete 是 FreeRTOS 中用于删除定时器的函数。它允许你从 FreeRTOS 系统中删除一个已经创建的定时器,并且可以指定一个最大等待时间来确保删除操作能够成功完成。

函数原型
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );
参数说明
  1. xTimer:

    • 这是要删除的定时器的句柄,类型为 TimerHandle_t。该句柄是在创建定时器时由 xTimerCreate 返回的。
  2. xTicksToWait:

    • 这是一个表示等待时间的参数,单位为 FreeRTOS 系统的 ticks。当调用 xTimerDelete 删除定时器时,如果定时器当前正在执行或处于活动状态,系统会在 xTicksToWait 指定的时间内等待,直到该定时器能够安全删除。如果超过指定时间还没有完成删除操作,则该函数会返回失败。
    • 通常设置为 portMAX_DELAY,表示无限期等待,直到定时器成功删除。
返回值
  • pdPASS:定时器删除成功。
  • errQUEUE_EMPTY 或其他错误代码:定时器删除失败,可能是由于定时器仍在运行或等待删除的条件未满足。
修改周期

xTimerChangePeriod 是 FreeRTOS 中用于更改定时器周期的函数。它允许在定时器创建之后修改其周期(时间间隔)。定时器的周期定义了定时器触发回调函数的间隔时间。

函数原型
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,TickType_t xNewPeriod,TickType_t xTicksToWait );
参数说明
  1. xTimer:

    • 要更改周期的定时器的句柄,类型为 TimerHandle_t。这个句柄是在创建定时器时由 xTimerCreate 返回的。
  2. xNewPeriod:

    • 新的周期值,单位是 FreeRTOS 的 ticks。该值定义了定时器触发回调函数的间隔时间。通过 pdMS_TO_TICKS 可以方便地将毫秒转换为 ticks
  3. xTicksToWait:

    • 这个参数表示在尝试更改定时器周期时,系统最多等待的时间,单位为 ticks。如果在指定的时间内无法完成周期更改,函数将返回错误。如果设置为 portMAX_DELAY,则表示无限期等待,直到成功更改定时器周期。
返回值
  • pdPASS:定时器周期更改成功。
  • errQUEUE_EMPTY 或其他错误代码:定时器周期更改失败,可能是由于定时器当前处于忙碌状态或其他错误条件。

增加游戏音效代码

beep.c
/*  Copyright (s) 2019 深圳百问网科技有限公司*  All rights reserved* * 文件名称:beep.c* 摘要:*  * 修改历史     版本号        Author       修改内容*--------------------------------------------------* 2023.9.08      v01         百问科技      创建文件*--------------------------------------------------
*/#include <stdlib.h>
#include <stdio.h>
#include <string.h>#include "cmsis_os.h"
#include "FreeRTOS.h"                   // ARM.FreeRTOS::RTOS:Core
#include "task.h"                       // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h"               // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h"                     // ARM.FreeRTOS::RTOS:Core#include "driver_passive_buzzer.h"static TimerHandle_t g_TimerSound;/*********************************************************************** 函数名称: GameSoundTimer_Func* 功能描述: 游戏声音的定时器函数,用来停止声音* 输入参数: 无* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/08	     V1.0	  韦东山	      创建***********************************************************************/
static void GameSoundTimer_Func( TimerHandle_t xTimer )
{PassiveBuzzer_Control(0);
}/*********************************************************************** 函数名称: buzzer_init* 功能描述: 初始化蜂鸣器并创建定时器(用于停止声音)* 输入参数: 无* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/08	     V1.0	  韦东山	      创建***********************************************************************/
void buzzer_init(void)
{/* 初始化蜂鸣器 */PassiveBuzzer_Init();	/* 创建定时器 */g_TimerSound = xTimerCreate( "GameSound", 200,pdFALSE,NULL,GameSoundTimer_Func);
}/*********************************************************************** 函数名称: buzzer_buzz* 功能描述: 启动蜂鸣器发出一段声音* 输入参数: freq - 声音频率*            time_ms - 持续时间* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/08	     V1.0	  韦东山	      创建***********************************************************************/
void buzzer_buzz(int freq, int time_ms)
{PassiveBuzzer_Set_Freq_Duty(freq, 50);/* 启动定时器 */xTimerChangePeriod(g_TimerSound, time_ms, 0);
}
game1.c
/** Project: N|Watch* Author: Zak Kemble, contact@zakkemble.co.uk* Copyright: (C) 2013 by Zak Kemble* License: GNU GPL v3 (see License.txt)* Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>#include "cmsis_os.h"
#include "FreeRTOS.h"                   // ARM.FreeRTOS::RTOS:Core
#include "task.h"                       // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h"               // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h"                     // ARM.FreeRTOS::RTOS:Core#include "draw.h"
#include "resources.h"#include "driver_lcd.h"
#include "driver_ir_receiver.h"
#include "driver_rotary_encoder.h"
#include "driver_mpu6050.h"#include "beep.h"#define NOINVERT	false
#define INVERT		true#define sprintf_P  sprintf
#define PSTR(a)  a#define PLATFORM_WIDTH	12
#define PLATFORM_HEIGHT	4
#define UPT_MOVE_NONE	0
#define UPT_MOVE_RIGHT	1
#define UPT_MOVE_LEFT	2
#define BLOCK_COLS		32
#define BLOCK_ROWS		5
#define BLOCK_COUNT		(BLOCK_COLS * BLOCK_ROWS)typedef struct{float x;float y;float velX;float velY;
}s_ball;static const byte block[] ={0x07,0x07,0x07,
};static const byte platform[] ={0x60,0x70,0x50,0x10,0x30,0xF0,0xF0,0x30,0x10,0x50,0x70,0x60,
};static const byte ballImg[] ={0x03,0x03,
};static const byte clearImg[] ={0,0,0,0,0,0,0,0,0,0,0,0,
};static bool btnExit(void);
static bool btnRight(void);
static bool btnLeft(void);
void game1_draw(void);static byte uptMove;
static s_ball ball;
static bool* blocks;
static byte lives, lives_origin;
static uint score;
static byte platformX;static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
static QueueSetHandle_t g_xQueueSetInput; /* 输入设备的队列集 */
static QueueHandle_t g_xQueuePlatform; /* 挡球板队列 */
static QueueHandle_t g_xQueueIR;
static QueueHandle_t g_xQueueRotary;
static QueueHandle_t g_xQueueMPU6050; /* MPU6050队列 *//* 挡球板任务 */
static void platform_task(void *params)
{byte platformXtmp = platformX;    struct input_data idata;// Draw platformdraw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);while (1){//if (0 == IRReceiver_Read(&dev, &data))xQueueReceive(g_xQueuePlatform, &idata, portMAX_DELAY);uptMove = idata.val;// Hide platformdraw_bitmap(platformXtmp, g_yres - 8, clearImg, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);// Move platformif(uptMove == UPT_MOVE_RIGHT)platformXtmp += 3;else if(uptMove == UPT_MOVE_LEFT)platformXtmp -= 3;uptMove = UPT_MOVE_NONE;// Make sure platform stays on screenif(platformXtmp > 250)platformXtmp = 0;else if(platformXtmp > g_xres - PLATFORM_WIDTH)platformXtmp = g_xres - PLATFORM_WIDTH;// Draw platformdraw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);draw_flushArea(platformXtmp, g_yres - 8, 12, 8);platformX = platformXtmp;            		}
}/*********************************************************************** 函数名称: ProcessIRData* 功能描述: 读取红外遥控器键值并转换为游戏控制键,写入挡球板队列* 输入参数: 无* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/02	     V1.0	  韦东山	      创建***********************************************************************/
static void ProcessIRData(void)
{struct ir_data idata;static struct input_data input;xQueueReceive(g_xQueueIR, &idata, 0);if (idata.val == IR_KEY_LEFT){input.dev = idata.dev;input.val = UPT_MOVE_LEFT;}else if (idata.val == IR_KEY_RIGHT){input.dev = idata.dev;input.val = UPT_MOVE_RIGHT;}else if (idata.val == IR_KEY_REPEAT){/* 保持不变 */;}else{input.dev = idata.dev;input.val = UPT_MOVE_NONE;}/* 写挡球板队列 */xQueueSend(g_xQueuePlatform, &input, 0);
}/*********************************************************************** 函数名称: ProcessRotaryData* 功能描述: 读取旋转编码器数据并转换为游戏控制键,写入挡球板队列* 输入参数: 无* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/02	     V1.0	  韦东山	      创建***********************************************************************/
static void ProcessRotaryData(void)
{struct rotary_data rdata;struct input_data idata;int left;int i, cnt;/* 读旋转编码器队列 */xQueueReceive(g_xQueueRotary, &rdata, 0);/* 处理数据 *//* 判断速度: 负数表示向左转动, 正数表示向右转动 */if (rdata.speed < 0){left = 1;rdata.speed = 0 - rdata.speed;}else{left = 0;}//cnt = rdata.speed / 10;//if (!cnt)//	cnt = 1;if (rdata.speed > 100)cnt = 4;else if (rdata.speed > 50)cnt = 2;elsecnt = 1;/* 写挡球板队列 */idata.dev = 1;idata.val = left ? UPT_MOVE_LEFT : UPT_MOVE_RIGHT;for (i = 0; i < cnt; i++){xQueueSend(g_xQueuePlatform, &idata, 0);}
}/*********************************************************************** 函数名称: ProcessMPU6050Data* 功能描述: 读取MPU6050D的角度值并转换为游戏控制键,写入挡球板队列* 输入参数: 无* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/05	     V1.0	  韦东山	      创建***********************************************************************/
static void ProcessMPU6050Data(void)
{struct mpu6050_data mdata;struct input_data idata;/* 读旋转编码器队列 */xQueueReceive(g_xQueueMPU6050, &mdata, 0);/* 处理数据 *//* 判断角度, 大于90度表示往左移动挡球板, 小于90度表示往右移动挡球板 */if (mdata.angle_x > 90){idata.val = UPT_MOVE_LEFT;}else if(mdata.angle_x < 90){idata.val = UPT_MOVE_RIGHT;}else{idata.val = UPT_MOVE_NONE;}/* 写挡球板队列 */idata.dev = 2;xQueueSend(g_xQueuePlatform, &idata, 0);
}/*********************************************************************** 函数名称: InputTask* 功能描述: 输入任务,检测多个输入设备并调用对应处理函数* 输入参数: params - 未使用* 输出参数: 无* 返 回 值: 无* 修改日期:      版本号     修改人	      修改内容* -----------------------------------------------* 2023/09/02	     V1.0	  韦东山	      创建***********************************************************************/
static void InputTask(void *params)
{QueueSetMemberHandle_t xQueueHandle;while (1){/* 读队列集, 得到有数据的队列句柄 */xQueueHandle = xQueueSelectFromSet(g_xQueueSetInput, portMAX_DELAY);if (xQueueHandle){/* 读队列句柄得到数据,处理数据 */if (xQueueHandle == g_xQueueIR){ProcessIRData();}else if (xQueueHandle == g_xQueueRotary){ProcessRotaryData();}			else if (xQueueHandle == g_xQueueMPU6050){ProcessMPU6050Data();}			}}
}void game1_task(void *params)
{		    g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();buzzer_init();/* 创建队列,队列集,创建输入任务InputTask */g_xQueuePlatform = xQueueCreate(10, sizeof(struct input_data));g_xQueueSetInput = xQueueCreateSet(IR_QUEUE_LEN + ROTARY_QUEUE_LEN + MPU6050_QUEUE_LEN);g_xQueueIR = GetQueueIR();g_xQueueRotary = GetQueueRotary();g_xQueueMPU6050 = GetQueueMPU6050();xQueueAddToSet(g_xQueueIR, g_xQueueSetInput);xQueueAddToSet(g_xQueueRotary, g_xQueueSetInput);xQueueAddToSet(g_xQueueMPU6050, g_xQueueSetInput);xTaskCreate(MPU6050_Task, "MPU6050Task", 128, NULL, osPriorityNormal, NULL);xTaskCreate(InputTask, "InputTask", 128, NULL, osPriorityNormal, NULL);uptMove = UPT_MOVE_NONE;ball.x = g_xres / 2;ball.y = g_yres - 10;ball.velX = -0.5;ball.velY = -0.6;
//	ball.velX = -1;
//	ball.velY = -1.1;blocks = pvPortMalloc(BLOCK_COUNT);memset(blocks, 0, BLOCK_COUNT);lives = lives_origin = 3;score = 0;platformX = (g_xres / 2) - (PLATFORM_WIDTH / 2);xTaskCreate(platform_task, "platform_task", 128, NULL, osPriorityNormal, NULL);while (1){game1_draw();//draw_end();vTaskDelay(50);}
}static bool btnExit()
{vPortFree(blocks);if(lives == 255){//game1_start();}else{//pwrmgr_setState(PWR_ACTIVE_DISPLAY, PWR_STATE_NONE);	//animation_start(display_load, ANIM_MOVE_OFF);vTaskDelete(NULL);}return true;
}static bool btnRight()
{uptMove = UPT_MOVE_RIGHT;return false;
}static bool btnLeft()
{uptMove = UPT_MOVE_LEFT;return false;
}void game1_draw()
{bool gameEnded = ((score >= BLOCK_COUNT) || (lives == 255));byte platformXtmp = platformX;static bool first = 1;// Move ball// hide balldraw_bitmap(ball.x, ball.y, clearImg, 2, 2, NOINVERT, 0);draw_flushArea(ball.x, ball.y, 2, 8);// Draw platform//draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);//draw_flushArea(platformX, g_yres - 8, 12, 8);if(!gameEnded){ball.x += ball.velX;ball.y += ball.velY;}bool blockCollide = false;const float ballX = ball.x;const byte ballY = ball.y;// Block collisionbyte idx = 0;LOOP(BLOCK_COLS, x){LOOP(BLOCK_ROWS, y){if(!blocks[idx] && ballX >= x * 4 && ballX < (x * 4) + 4 && ballY >= (y * 4) + 8 && ballY < (y * 4) + 8 + 4){
//				buzzer_buzz(100, TONE_2KHZ, VOL_UI, PRIO_UI, NULL);buzzer_buzz(2000, 100);// led_flash(LED_GREEN, 50, 255); // 100ask todoblocks[idx] = true;// hide blockdraw_bitmap(x * 4, (y * 4) + 8, clearImg, 3, 8, NOINVERT, 0);                draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                blockCollide = true;score++;}idx++;}}// Side wall collisionif(ballX > g_xres - 2){if(ballX > 240)ball.x = 0;		elseball.x = g_xres - 2;ball.velX = -ball.velX;		}if(ballX < 0){ball.x = 0;		ball.velX = -ball.velX;	}// Platform collisionbool platformCollision = false;if(!gameEnded && ballY >= g_yres - PLATFORM_HEIGHT - 2 && ballY < 240 && ballX >= platformX && ballX <= platformX + PLATFORM_WIDTH){platformCollision = true;// buzzer_buzz(200, TONE_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todobuzzer_buzz(5000, 200);ball.y = g_yres - PLATFORM_HEIGHT - 2;if(ball.velY > 0)ball.velY = -ball.velY;ball.velX = ((float)rand() / (RAND_MAX / 2)) - 1; // -1.0 to 1.0}// Top/bottom wall collisionif(!gameEnded && !platformCollision && (ballY > g_yres - 2 || blockCollide)){if(ballY > 240){// buzzer_buzz(200, TONE_2_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todobuzzer_buzz(2500, 200);ball.y = 0;}else if(!blockCollide){// buzzer_buzz(200, TONE_2KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todobuzzer_buzz(2000, 200);ball.y = g_yres - 1;lives--;}ball.velY *= -1;}// Draw balldraw_bitmap(ball.x, ball.y, ballImg, 2, 2, NOINVERT, 0);draw_flushArea(ball.x, ball.y, 2, 8);// Draw platform//draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);//draw_flushArea(platformX, g_yres - 8, 12, 8);if (first){first = 0;// Draw blocksidx = 0;LOOP(BLOCK_COLS, x){LOOP(BLOCK_ROWS, y){if(!blocks[idx]){draw_bitmap(x * 4, (y * 4) + 8, block, 3, 8, NOINVERT, 0);draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                }idx++;}}}// Draw scorechar buff[6];sprintf_P(buff, PSTR("%u"), score);draw_string(buff, false, 0, 0);// Draw livesif(lives != 255){LOOP(lives_origin, i){if (i < lives)draw_bitmap((g_xres - (3*8)) + (8*i), 1, livesImg, 7, 8, NOINVERT, 0);elsedraw_bitmap((g_xres - (3*8)) + (8*i), 1, clearImg, 7, 8, NOINVERT, 0);draw_flushArea((g_xres - (3*8)) + (8*i), 1, 7, 8);    }}   // Got all blocksif(score >= BLOCK_COUNT)draw_string_P(PSTR(STR_WIN), false, 50, 32);// No lives left (255 because overflow)if(lives == 255)draw_string_P(PSTR(STR_GAMEOVER), false, 34, 32);}

相关文章:

任务通知的本质(任务通知车辆运行) 软件定时器的本质(增加游戏音效)

任务通知的本质 没有任务通知 所谓"任务通知"&#xff0c;你可以反过来读"通知任务"。 我们使用队列、信号量、事件组等等方法时&#xff0c;并不知道对方是谁。使用任务通知时&#xff0c;可 以明确指定&#xff1a;通知哪个任务。 使用队列、信号量、…...

如何在 MySQL 中进行数据导入和导出?

在 MySQL 中进行数据的导入和导出是一项常见的任务&#xff0c;用于数据备份、恢复、迁移以及数据分析等多种用途。MySQL 提供了多种方法来进行数据的导入和导出&#xff0c;每种方法都有其适用的场景和特点。以下是几种常用的 MySQL 数据导入和导出方法&#xff0c;包括命令行…...

python语言基础-5 进阶语法-5.3 流式编程

声明&#xff1a;本内容非盈利性质&#xff0c;也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站&#xff0c;会尽量附上原文链接&#xff0c;并鼓励大家看原文。侵删。 5.3 流式编程&#xff08;参考链接&#xff1a;https://www.zhihu.com/question/59062…...

centos 服务器 docker 使用代理

宿主机使用代理 在宿主机的全局配置文件中添加代理信息 vim /etc/profile export http_proxyhttp://127.0.0.1:7897 export https_proxyhttp://127.0.0.1:7897 export no_proxy"localhost,127.0.0.1,::1,172.171.0.0" docker 命令使用代理 例如我想在使用使用 do…...

[个人专属博客] - docker安装

&#x1f389;&#x1f389;&#x1f389;&#x1f389;&#x1f389;&#x1f389; 欢迎访问的个人博客&#xff1a;http://swzbk.site/&#xff0c;加好友&#xff0c;一起赚&#x1f9e7;&#x1f9e7;&#x1f9e7; &#x1f389;&#x1f389;&#x1f389;&#x1f389;&…...

推荐一个QDirStat基于 Qt 的目录统计工具

QDirStat 是一款基于 Qt 的目录统计工具&#xff0c;旨在帮助用户分析磁盘空间使用情况并进行清理。QDirStat的一些主要特点和功能&#xff1a; 跨平台兼容性&#xff1a;QDirStat 支持 Linux、BSD、Unix-like 系统以及 macOS&#xff0c;确保了广泛的用户基础。 直观的数据展…...

yolo自动化项目实例解析(九) 导航

比如我们经常使用的导航&#xff0c;说白了就是寻找两点之间最近的路径&#xff0c;也就是所谓的寻路&#xff0c;我们需要想办法让程序知道他要去哪里&#xff0c;路径包含&#xff08;起点、轨迹、终点&#xff09; 一、录制轨迹 从平面角度来看&#xff0c;我们可以把区域视…...

MySQL 报错:1137 - Can‘t reopen table

MySQL 报错&#xff1a;1137 - Can’t reopen table 1. 问题 对临时表查询&#xff1a; select a.ts_code,a.tsnum,b.tsnum from (select t.ts_code ,count(*) tsnum from tmp_table t group by t.ts_code having count(*) > 20 and count(*)< 50 ) a ,(select t.ts_…...

Bokeh实现大规模数据可视化的最佳实践

目录 引言 一、Bokeh简介 二、安装Bokeh 三、数据准备 四、性能优化 五、创建图表 六、添加交互功能 七、应用案例 八、高级技巧 九、总结 引言 在数据科学领域,数据可视化是一个至关重要的环节。通过可视化,我们可以直观地理解数据的特征和趋势,为数据分析和决策…...

HTMLCSS:比赛记分卡

效果演示 这段 HTML 和 CSS 代码创建了一个卡片式的体育比赛信息展示组件&#xff0c;用于显示篮球比赛的两个队伍名称、比赛时间、比分以及一些装饰性的视觉元素。 HTML <div class"card"><div data-status"inprogress" class"teams"…...

什么是 Faiss?

好的&#xff0c;我来详细解释 Faiss&#xff0c;它的用途、使用场景&#xff0c;以及如何安装和使用。 什么是 Faiss&#xff1f; Faiss 是由 Facebook AI Research 开发的一个开源库&#xff0c;专门用于高效的相似性搜索和聚类。它非常擅长在高维向量空间中进行快速搜索&a…...

【prism】遇到一个坑,分享!

背景 我通用prism的方式写了一个弹窗,弹窗绑定一个 Loaded 事件,但是Loaded事件一直不触发!!! 具体过程 我的loaded事件也是通过命令的方式绑定的: <i:Interaction.Triggers><i:EventTrigger EventName="Loaded...

vue制作代码比较工具

前两天朋友问我 有没有vue可以做一个json代码在线比较工具 我也是在网上搜了一下找到的 废话不说 直接上代码 采用 v3 pnpm i v-code-diff <div><CodeDiff:old-string"oldStr":new-string"newStr"output-format"side-by-side"/>…...

GPT系列文章

GPT系列文章 GPT1 GPT1是由OpenAI公司发表在2018年要早于我们之前介绍的所熟知的BERT系列文章。总结&#xff1a;GPT 是一种半监督学习&#xff0c;采用两阶段任务模型&#xff0c;通过使用无监督的 Pre-training 和有监督的 Fine-tuning 来实现强大的自然语言理解。在 Pre-t…...

Qt实现可拖拽的矩形

之前项目上需要用Qt来绘制可拖拽改变形状的矩形。看了Qt Graphics相关的内容&#xff0c;虽然对Qt怎么添加图元的有了些了解&#xff0c;但是具体如何实现拖拽效果&#xff0c;一时也没有什么好的想法。还好网上有人分享的例子&#xff0c;很受启发。后来又回顾了一下这部分的代…...

python爬虫初体验(五)—— 边学边玩小游戏

1. 打开浏览器 利用webbrowser 模块的 open()函数可以启动一个新浏览器&#xff0c;打开指定的 URL。 import webbrowser webbrowser.open(http://inventwithpython.com/) 2. 猜数字游戏 # -*- coding: utf-8 -*- # This is a guess the number game. import randomsecretN…...

学习日志015--python单链表

创建 class Node:def __init__(self,data):# 数据域self.data data# 链接域self.next Noneclass LinkList:def __init__(self,):# 初始化头节点self.head None# 记录链表的长度self.size 0 增加 #头插def insert_head(self,value):# 创建新节点node Node(value)q self…...

51WORLD与南京水利研究院联合研发,国产数字孪生超融合一体机

近日&#xff0c;太湖流域水治理国际会议在江苏省无锡市举行。大会由水利部国际合作与科技司、河湖管理司、中国水利学会、水利部太湖流域管理局、无锡市人民政府、中国交通建设集团有限公司指导&#xff0c;南京水利科学研究院主办&#xff0c;以“践行新发展理念、推进流域水…...

自动泊车变自动撞车?小米SU7遭遇批量事故

科技新知 原创作者丨依蔓 编辑丨蕨影 小米系统bug&#xff0c;70多辆小米SU7同一天自动泊车撞墙、撞柱&#xff01; 近日&#xff0c;多名车主反映小米汽车SU7标准版“自动泊车”功能出现故障&#xff0c;造成不同程度的撞击、剐蹭损伤。 小米客服此前回应涉事车主&#xff0…...

异常和中断

在计算机系统中&#xff0c;异常和中断是两种常见的用于处理异步事件的机制。以下是常见的异常和中断及其特点的详细解释&#xff1a; 异常&#xff08;内中断&#xff09; 异常&#xff0c;也称为内中断&#xff0c;是由CPU内部事件引起的中断。异常通常与程序执行的当前指令…...

代理IP在后端开发中的应用与后端工程师的角色

目录 引言 代理IP的基本概念和工作原理 代理IP在后端开发中的应用 网络爬虫与数据采集 负载均衡与性能优化 安全防护与隐私保护 后端工程师在使用代理IP时面临的挑战 结论 引言 在数字化时代&#xff0c;网络技术的飞速发展极大地推动了各行各业的发展。其中&#xff…...

设计模式之 观察者模式

观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听一个主题对象&#xff08;Subject&#xff09;。当主题对象的状态发生变化时&#xff0c;所有依赖于它的观察者都会得到…...

SQLSever显示物理和逻辑 IO活动量的相关信息及显示分析、编译和执行各语句所需的毫秒数。

SQLSever SET STATISTICS IO显示物理和逻辑 IO活动量的相关信息及SET STATISTICS TIME显示分析、编译和执行各语句所需的毫秒数。 1、 SET STATISTICS IO (Transact-SQL) 物理 IO 与访问磁盘上的数据页相关&#xff0c;逻辑 IO 与访问内存中的数据页&#xff08;数据缓存&…...

CSS3 动画:前端开发的动态美

CSS3 动画:前端开发的动态美 CSS3 动画是现代网页设计中不可或缺的一部分,它为静态的网页元素添加了动态效果,提升了用户体验。本文将深入探讨CSS3动画的基础知识、高级技巧,并展示如何在实际项目中应用这些动画。 CSS3 动画基础 CSS3动画主要通过@keyframes和动画属性(…...

JavaWeb之综合案例

前言 这一节讲一个案例 1. 环境搭建 然后就是把这些数据全部用到sql语句中执行 2.查询所有-后台&前台 我们先写后台代码 2.1 后台 2.2 Dao BrandMapper&#xff1a; 注意因为数据库里面的名称是下划线分割的&#xff0c;我们类里面是驼峰的&#xff0c;所以要映射 …...

基于Spring Boot+Unipp的博物馆预约小程序(协同过滤算法、二维码识别)【原创】

&#x1f388;系统亮点&#xff1a;协同过滤算法、二维码识别&#xff1b; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框…...

使用 Maven 构建一个简单的 Java 项目

Apache Maven 是一个强大的构建自动化工具&#xff0c;主要用于 Java 项目。它简化了构建和管理任何基于 Java 的项目的流程。 本指南将涵盖 Maven 的安装、设置一个简单的 Java 项目以及使用 Maven 运行该项目。 1. 安装 安装 Java 在安装 Maven 之前&#xff0c;需要确保…...

【51单片机】LCD1602液晶显示屏

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 LCD1602存储结构时序结构 编码 —— 显示字符、数字 LCD1602 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是…...

UDP协议

UDP&#xff08;UserDatagramProtocol&#xff09;是一个简单的传输层协议&#xff0c;特点&#xff1a;无连接、不可靠、面向数据包、全双工。 报文结构 1.源端口&#xff1a; 源端口号&#xff0c;需要对方回信时选用&#xff0c;不需要时全部置0. 2.目的端口&#xff1a;目…...

阅读 ADiffusion-Based Framework for Multi-Class Anomaly Detection

A Diffusion-Based Framework for Multi-Class Anomaly Detection 我觉得引言部分写的不错&#xff0c;将问题清楚的讲出来了&#xff0c;值得借鉴&#xff01;&#xff01; 摘要 基于重建的方法在异常检测方面取得了显著成果。最近流行的扩散模型的卓越图像重建能力引发了研…...

网络云计算】2024第47周-每日【2024/11/21】周考-实操题-RAID6实操解析1

文章目录 1、RAID6配置指南&#xff08;大致步骤&#xff09;2、注意事项3、截图和视频 网络云计算】2024第47周-每日【2024/11/21】周考-实操题-RAID6实操 RAID6是一种在存储系统中实现数据冗余和容错的技术&#xff0c;其最多可以容忍两块磁盘同时损坏而不造成数据丢失。RAID…...

ts- declare关键词及vue3报错“Window typeof globalThis”上不存在属性“nextLoading”、`

报错“Window & typeof globalThis”上不存在属性“nextLoading”、 代码环境&#xff1a;vue3、ts 阮一峰讲解 declarets 用法告诉编译器某个类型是存在的 下面的例子是脚本使用浏览器全局对象document。 declare var document; document.title "Hello";上面…...

【STM32】在 STM32 USB 设备库添加新的设备类

说实话&#xff0c;我非常想吐槽 STM32 的 USB device library&#xff0c;总感觉很混乱。 USB Device library architecture 根据架构图&#xff1a; Adding a custom class 如果你想添加新的设备类&#xff0c;必须修改的文件有 usbd_desc.cusbd_conf.cusb_device.c 需要…...

【单点知识】基于PyTorch讲解自动编码器(Autoencoder)

文章目录 0. 前言1. 自动编码器的基本概念1.1 定义1.2 目标1.3 结构 2. PyTorch实现自动编码器2.1 导入必要的库2.2 定义自动编码器模型2.3 加载数据2.4 训练自动编码器 3. 自动编码器的意义4. 自动编码器的应用4.1 图像处理4.2自然语言处理&#xff1a;4.3推荐系统&#xff1a…...

html+js实现图片的放大缩小等比缩放翻转,自动播放切换,顺逆时针旋转

效果图&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>图片预览</title><sty…...

【蓝桥杯C/C++】翻转游戏:多种实现与解法解析

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: 蓝桥杯C/C 文章目录 &#x1f4af;题目&#x1f4af;问题分析解法一&#xff1a;减法法解法二&#xff1a;位运算解法解法三&#xff1a;逻辑非解法解法四&#xff1a;条件运算符解法解法五&#xff1a;数组映射法不同解法的比较…...

介绍一下toupper(ch);函数(c基础)

hi , I am 36 适合对象c语言初学者 toupper(ch1); tolower(ch2); 是返回ch的大写或小写的字符但并不改变ch 若传递数字仍返回该数字 格式 #include<ctype.h> char res toupper(ch); 链接扫雷游戏代码分享(c基础)-CSDN博客 hi , I am 36. thanks for your look…...

如何使用Python代码实现给GPU预加热

如何使用Python代码实现给GPU预加热 一、引言二、使用深度学习框架进行预加热2.1 TensorFlow预加热2.2 PyTorch预加热三、使用CUDA进行预加热四、预加热的效果评估与优化五、结论与展望在高性能计算和深度学习领域,GPU(图形处理器)已经成为不可或缺的加速工具。然而,在实际…...

基于 SpringBoot 的作业管理系统【附源码】

基于 SpringBoot 的作业管理系统 效果如下&#xff1a; 系统注册页面 学生管理页面 作业管理页面 作业提交页面 系统管理员主页面 研究背景 随着社会的快速发展&#xff0c;信息技术的广泛应用已经渗透到各个行业。在教育领域&#xff0c;课程作业管理是学校教学活动中的重要…...

LeetCode题解:26.删除有序数组中的重复项【Python题解超详细,双指针法】,知识拓展:原地修改

题目描述 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &…...

docker 容器运行Ruoyi-cloud

1&#xff0c;linux系统安装openjdk1.8,mvn,dokcer,node,git 2&#xff0c;拉取代码 1&#xff09;查看gitee仓库地址 2&#xff09;创建/app文件夹&#xff0c;进入app目录 mkdir /app cd /app 3&#xff09;clone代码 4&#xff09;修改配置文件中nacos地址 # 修改注…...

【Unity How】Unity中如何实现物体的匀速往返移动

直接上代码 using UnityEngine;public class CubeBouncePingPong : MonoBehaviour {[Header("移动参数")][Tooltip("移动速度")]public float moveSpeed 2f; // 控制移动的速度[Tooltip("最大移动距离")]public float maxDistance 5f; // 最大…...

STM32完全学习——系统时钟设置

一、时钟框图的解读 首先我们知道STM32在上电初始化之后使用的是内部的HSI未经过分频直接通过SW供给给系统时钟&#xff0c;由于内部HSI存在较大的误差&#xff0c;因此我们在系统完成上电初始化&#xff0c;之后需要将STM32的时钟切换到外部HSE作为系统时钟&#xff0c;那么我…...

简单理解下基于 Redisson 库的分布式锁机制

目录 简单理解下基于 Redisson 库的分布式锁机制代码流程&#xff1a;方法的调用&#xff1a;具体锁的实现&#xff1a;riderBalance 方法&#xff1a;tryLock 方法&#xff08;重载&#xff09;&#xff1a;tryLock 方法&#xff08;核心实现&#xff09;&#xff1a; 简单理解…...

ruoyi框架完成分库分表,按月自动建表功能

前提 这个分库分表功能&#xff0c;按月自动建表&#xff0c;做的比较久了&#xff0c;还没上线&#xff0c;是在ruoyi框架内做的&#xff0c;踩了不少坑&#xff0c;但是已经实现了&#xff0c;就分享一下代码吧 参考 先分享一些参考文章 【若依系列】集成ShardingSphere S…...

数据结构 【单链表练习】

今天来探讨两个练习题要使用的思想为快慢指针。 1、返回链表的中间节点 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。如果有两个中间结点&#xff0c;则返回第二个中间结点。 整体思路如下图所示&#xff1a; 代码如下&#xff1a; /*** Definition f…...

wsl虚拟机中的dockers容器访问不了物理主机

1 首先保证wsl虚拟机能够访问宿主机IP地址&#xff0c;wsl虚拟机通过vEthernet (WSL)的地址访问&#xff0c;着意味着容器也要通过此IP地址访问物理主机。 2 遇到的问题&#xff1a;wsl虚拟机中安装了docker&#xff0c;用在用到docker容器内的开发环境&#xff0c;但是虚拟机…...

Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持

作者&#xff1a;来自 Elastic Saikat Sarkar 使用 Elasticsearch 向量数据库构建搜索 AI 体验时如何使用 IBM watsonx™ Slate 文本嵌入。 Elastic 很高兴地宣布&#xff0c;通过集成 IBM watsonx™ Slate 嵌入模型&#xff0c;我们的开放推理 API 功能得以扩展&#xff0c;这…...

【如何用更少的数据作出更好的决策】-gpt生成

如何用更少的数据作出更好的决策 用更少的数据作出更好的决策是一种能力的体现&#xff0c;需要结合有效的方法、严谨的逻辑以及对问题的深刻理解。以下是一些可以帮助你实现这一目标的策略&#xff1a; 明确目标 在收集和分析数据之前&#xff0c;先明确你的决策目标是什么…...

webview4/edgewebbrower学习记录——执行js

webview2可执行js方法&#xff1a;WVBrowser1.ExecuteScript(js, 1003) 参数1为js语句&#xff0c;参数2为命令号&#xff0c;执行完毕&#xff0c;会执行 procedure TBrowserFrame.WVBrowser1ExecuteScriptCompleted(Sender: TObject; aErrorCode: HRESULT; const aResultOb…...