Предварительные знания
OSAL (Operating System Abstraction Layer, уровень системной абстракции) в целом можно понимать как упрощенную версию операционной системы, которая обеспечивает базовые функции, такие как управление памятью, управление прерываниями и планирование задач для корректной работы Z-Stack.
-
Идентификатор задачи: прикладной уровень — это задача, которой система присваивает уникальный числовой номер.
-
Функция обработки событий задачи: задачи могут обрабатывать события, а коды для обработки событий находятся в одной функции.
-
Переменная события задачи. Задача прикладного уровня также имеет 2-байтовую переменную.
-
Связь между переменной события задачи прикладного уровня и событием , определенным на уровне приложения [ранее заданным с помощью макроса, две байтовые переменные, определенные для представления этого события]
- Если переменная события, значение макроса и операция события равны 1, это означает, что задача прикладного уровня будет обрабатывать это событие.
-
Когда система работает, она продолжает считывать переменную события задачи прикладного уровня. Когда она обнаруживает, что эта переменная равна 0, она думает, что задача прикладного уровня в настоящее время не имеет событий для обработки; если она обнаруживает, что эта переменная не равна 0 , он думает, что уровень приложения. Когда задача имеет событие для обработки, она вызывает функцию обработки событий задачи уровня приложения
TestApp_ProcessEvent( byte task_id, UINT16 events )
и передает значение переменной события задачиevents
. -
В функции обработки событий
events
он будет оперировать всеми значениями макросов событий, определенными прикладным уровнем. Если окажется, что оно равно 0, то будет выполнен соответствующий код для этой обработки событий. -
osal_set_event()
Вызов функции события set TaskTestApp_TaskID
выполнитTestApp_SEND_MSG_EVT
соответствующий код обработки события.- Суть: Измените
TestApp_TaskID
переменную события задачи (задача прикладного уровня), соответствующуюTestApp_SEND_MSG_EVT
значению макроса от 1 до 1. Таким образом, сначала переменная события задачи становится отличной от 0, и система обнаруживает, что переменная события задачи не равна 0. ., будет вызвана функция обработки события задачиTestApp_ProcessEvent
. В этой функции переменная события задачи,TestApp_SEND_MSG_EVT
значение макроса и операция должны быть равны 1, поэтому код, связанный с этим событием, обязательно будет выполнен.
- Суть: Измените
-
Каждый уровень представляет собой задачу, поэтому каждый уровень имеет идентификатор задачи. Переменная события задачи обработчика событий задачи
- FUN function array={обработка события задачи A, обработка события задачи B, обработка события задачи C...}
- Массив переменных Arr = {переменная события задачи a, переменная события задачи b, переменная события задачи c...}
-
Идентификатор задачи не назначается системой случайным образом. Вы можете использовать это значение, чтобы сразу найти собственную функцию обработки событий задачи и переменные событий .
- Например: идентификатор задачи прикладного уровня — 8.
- Тогда FUN[8] содержит имя функции соответствующей функции обработки задачи.
- Arr[8] содержит переменную события задачи.
Определение профессионализма из других источников:
Задача : можно понимать как конкретную задачу, для обработки которой требуется процессор, например «включить свет через 1 секунду» или «выключить свет» и так далее.
Пул задач : это буфер, в котором можно хранить несколько задач. Например, пул задач может хранить «Включить свет через 1 секунду», «Выключить свет через 2 секунды», «Включить свет через 3 секунды». и «Выключить свет через 1 минуту». «Легкие» задания. Система выполнит каждую задачу в пуле задач в указанное время.
Приоритет : поскольку может быть несколько задач, которые необходимо выполнить одновременно, необходимо различать, какие задачи имеют приоритет, а какие задачи откладываются в данный момент.Приоритет используется для обозначения уровня приоритета каждой задачи. При тех же условиях система будет отдавать приоритет задачам с высоким приоритетом, а задачам с низким приоритетом придется ждать обработки. Кроме того, система может прерывать выполнение задач с более низким приоритетом и переключаться на задачи с более высоким приоритетом.
Опрос : система время от времени проверяет в пуле задач наличие задач, которые необходимо обработать сейчас.Этот процесс называется опросом.
Цикл планирования операционной системы . Цикл планирования относится к определенной продолжительности времени « время от времени » в концепции опроса. Период планирования системы также является минимальным периодом времени выполнения задачи.Например, период планирования системы составляет 1 секунду, но есть задача, которая «выключает свет через 0,1 секунды».Хотя эта задача требует выключения света после 0,1 секунды, 0,1 секунды меньше периода планирования системы. , поэтому эта задача будет выполнена через 1 секунду.
Практическое планирование системы
osal_init_system
В функции osal_init_system
есть функция osalInitTasks();
, в которой система присваивает всем задачам идентификаторы.
void osalInitTasks( void ) // 定义函数 osalInitTasks,该函数不接受任何参数并且没有返回值
{
uint8 taskID = 0; // 定义一个 uint8 类型的变量 taskID 并初始化为 0,用于表示任务ID
// 为 tasksEvents 分配内存,tasksCnt 是任务的数量,这里分配的是 uint16 类型的大小乘以 tasksCnt 的大小
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
// 将分配的内存区域用 0 填充
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
// 初始化 macTask,并使用 ++taskID 作为参数
macTaskInit( taskID++ );
// 初始化 nwk,并使用 ++taskID 作为参数
nwk_init( taskID++ );
// 初始化 Hal,并使用 ++taskID 作为参数
Hal_Init( taskID++ );
#if defined( MT_TASK ) // 如果定义了 MT_TASK
// 初始化 MT_Task,并使用 ++taskID 作为参数
MT_TaskInit( taskID++ );
#endif
// 初始化 APS,并使用 ++taskID 作为参数
APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION ) // 如果定义了 ZIGBEE_FRAGMENTATION
// 初始化 APSF,并使用 ++taskID 作为参数
APSF_Init( taskID++ );
#endif
// 初始化 ZDApp,并使用 ++taskID 作为参数
ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
// 如果定义了 ZIGBEE_FREQ_AGILITY 或者 ZIGBEE_PANID_CONFLICT
// 初始化 ZDNwkMgr,并使用 ++taskID 作为参数
ZDNwkMgr_Init( taskID++ );
#endif
// 初始化 TestAPP,没有提供任务ID,可能是最后初始化的任务,或者是之前的任务ID都在其他任务中使用了
TestAPP_Init( taskID );
}
osal_start_system
Роль цикла do- while:
Опросите весь пул задач, то есть посмотрите, есть ли какие-нибудь задачи, требующие обработки. В цикле есть только одно условное суждение, если условие истинно, то цикл завершается.
Среди них TaskEvents — это массив типа uint16, каждый элемент которого представляет тип задачи.То есть, TaskSEvents — это пул задач, а TasksCnt — размер этого пула задач.
Логика работы этого цикла следующая:
- Во-первых, начальное значение idx равно 0;
- Когда значение TasksEvents[idx] равно 0, это означает, что в задаче нечего обрабатывать. В этот момент условное суждение не устанавливается и вводится следующий цикл;
- Перед каждым выполнением цикла idx увеличивается на 1, а затем определяется, меньше ли он задачCnt;
- Когда значение TasksEvents[idx] не равно 0, это означает, что в задаче есть что-то, что нужно обработать.В этот момент устанавливается условное суждение, поэтому цикл завершается прерыванием;
- Когда цикл завершится, если во всем пуле задач нет задач для обработки, то idx обязательно будет >= TasksCnt. Следовательно, если idx < TasksCnt, это означает, что в пуле задач есть задачи, которые необходимо обработать, азадачиEvents[idx] — это задача, которую необходимо обработать в данный момент. Поэтому после завершения цикла Z-Stack сначала использует оператор if (idx < TasksCnt), чтобы определить, есть ли задачи, которые необходимо обработать.
/*
* 这是一个不返回的函数,是任务系统的主循环函数。它将遍历所有任务事件并为有事件的任务调用task_event_processor()函数。
* 如果所有任务都没有事件,此函数会让处理器进入睡眠模式。
*/
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT ) // 如果未定义ZBIT和UBIT宏
for(;;) // 进入无限循环,除非外部中断或者其他条件停止
#endif
{
uint8 idx = 0; // 定义一个uint8类型的变量idx并初始化为0,用于表示任务索引
osalTimeUpdate(); // 更新系统时间
Hal_ProcessPoll(); // 执行Hal的轮询处理,这替换了MT_SerialPoll()和osal_check_timer()函数
// 开始do-while循环,直到idx达到tasksCnt(任务数量)为止
do {
if (tasksEvents[idx]) // 如果当前任务有事件(事件为非零值)
{
break; // 则跳出循环,执行后续代码
}
} while (++idx < tasksCnt); // idx自增1并判断是否小于tasksCnt,若小于则继续循环
// 如果idx小于tasksCnt(还有任务未处理)
if (idx < tasksCnt)
{
uint16 events; // 定义一个uint16类型的变量events,用于存储事件
halIntState_t intState; // 定义一个halIntState_t类型的变量intState,用于存储中断状态(用于锁住临界区)
HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区,锁定中断以保护临界区代码的执行,将intState设置为当前的中断状态
events = tasksEvents[idx]; // 从tasksEvents数组中获取当前任务的事件
tasksEvents[idx] = 0; // 清零当前任务的未处理事件(清除事件)
HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区,解锁中断恢复原始的中断状态
// 调用tasksArr数组中对应任务的回调函数,参数为当前任务的索引idx和已处理的事件events,并将返回的事件添加到当前任务的事件中
events = (tasksArr[idx])( idx, events );
HAL_ENTER_CRITICAL_SECTION(intState); // 再次进入临界区,锁定中断以保护临界区代码的执行,将intState设置为当前的中断状态
tasksEvents[idx] |= events; // 将未处理的事件添加回当前任务的事件中(添加未处理事件)
HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区,解锁中断恢复原始的中断状态
}
#if defined( POWER_SAVING ) // 如果定义了POWER_SAVING宏(开启节能模式)
else // 如果整个任务事件循环没有任何活动(没有任务有事件)
{
osal_pwrmgr_powerconserve(); // 则调用osal_pwrmgr_powerconserve()函数将处理器/系统置于睡眠模式以节省电力(节能模式)
}
#endif // 结束if宏定义判断(如果有定义POWER_SAVING宏)
} // 结束for循环(整个任务事件处理流程)
} // 结束函数定义(osal_start_system函数)