管理定时器和实时定时器
管理时间数据
-
在 Kithara RealTime Suite 中,所有时间值均以 100 ns 为单位给出:这不仅适用于定时器的安装,也适用于阻塞函数的超时值 (‘KS_waitForEvent’) 等。
-
为了在对源代码进行编程时使用可管理的数字,可以给出倍数为 10000 的毫秒值。为了进一步简化此过程,还可以在程序代码的中心位置预定义值 10000,例如:
const int ms = 10000;
int timeout = 200 * ms;
...
通用定时器编程
要创建计时器,您需要创建一些依赖对象:
-
事件对象 (KS_createEvent)
-
回调对象 (KS_createCallBack)
-
任务 (KS_createTask)
计时器安装需要生成的句柄。如果句柄指向回调或任务对象,则当计时器触发时,将执行此对象的例程。在这种情况下,是要确保使用的所有数据和对象都位于同一个地址空间(ring3 或 ring0)中!
初始化计时器
可以使用函数 KS_createTimer 初始化计时器。可编程定时器频率取决于可用的功能。使用定时器模块,可以以近似毫秒的间隔对定时器周期进行编程,使用实时模块可以编程更高的频率。RealTime 模块是自定义驱动程序的可选组件。
非实时计时器的延迟周期始终四舍五入到 1 毫秒。如果创建的计时器不是一次性计时器,它将一直运行,直到计时器函数返回不等于 0 的值(例如错误值)。尽管存在中断,但稍后仍必须使用 KS_removeTimer 删除计时器。对于频繁的停止和重新启动计时器,建议改用 KS_startTimer/KS_stopTimer。
删除计时器
如果不再使用计时器对象,则必须将其删除。若要删除计时器对象并释放所有已用资源,必须调用 KS_removeTimer。
停止/启动/调整定时器
可以使用 KS_stopTimer 随时停止计时器。定时器在调用"KS_stopTimer"时运行,这不会影响定时器功能,只会停止任何进一步的信号。
要重新启动或首次启动使用标志"KSF_DONT_START"创建的计时器,应使用函数 KS_startTimer。使用"KS_startTimer"也可以编程新周期。如果计时器未停止并调用"KS_startTimer",则计时器将使用新周期重新启动。
要在不重新启动计时器的情况下调整计时器周期,可以使用 KS_adjustTimer。新周期用于下一个周期。
备注
-
稍后启动计时器:在各种情况下,首先安装计时器并稍后启动它可能会很有用。为此,在安装回调函数时会提供"KSF_DONT_START"标志。在这种情况下,准备好的计时器处于休眠状态,将等待函数"KS_startTimer"的调用。优点:分配资源的耗时过程已经完成,计时器可以更快地启动。
-
只需一次计时器调用:标志"KSF_ONE_SHOT"可用于"KS_createTimer"和"KS_startTimer"功能,以确保计时器只会发射一次(“一发”)。使用"KS_startTimer"也可以在每次呼叫时提供新的延迟。
-
看门狗监控:要重新启动看门狗计时器,可以调用"KS_startTimer"。
RealTimer 备注
要求:
-
时钟模块和实时模块可用
-
PC 使用 APIC 控制器(高级可编程中断控制器)。所有多核 CPU 都是这种情况,包括超线程处理器和 SMP 计算机,以及最新的单核 CPU。
-
必须使用适合内核的对象(例如,使用"KSF_DIRECT_EXEC"创建的回调或内核任务)。
-
硬件定时器频率高的定时器必须特别小心地编程:确定是否应执行已安装的软件定时器功能需要 CPU 性能。这种对基本系统的影响可能会占用 CPU 性能的百分之几或更多。这可能导致系统不稳定。因此,在不同的 PC 系统上运行的能力可能会有所不同!超过 100 kHz 的频率通常是不现实的。但大多数情况下 10 kHz 应该没有问题。
-
当前执行的实时定时器例程不会自行中断。这意味着在安装的不同实时计时器之间没有优先级。因此,在实时定时器可能出现抖动的情况下,必须考虑安装的其他实时定时器例程的执行时间。实时定时器本身在系统中具有最高优先级,并且只能通过禁用中断接受来延迟。可以使用多任务模块实现运行优先级代码,有关详细信息,请参阅:使用多个任务(优先级、信号量、多个 CPU 内核)进行编程
项目实例
项目结构
项目源码
SharedData.h
#pragma once
#include <KrtsDemo.h>
// 内核层数据需要1字节对齐
#pragma pack(1)
constexpr int CONTROL_PERIOD{ 1000 }; // 控制周期 单位us
typedef struct SharedData {
KSHandle timer_call_back = NULL; // 定时器回调
KSHandle timer_handle = NULL; // 定时器
int timer_count; // 计时器触发次数
}SharedData;
#pragma pack()
KitharaDemo.cpp
#include <iostream>
#include "../KitharaDll/SharedData.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
const char customerNumber[256] = "DEMO";
SharedData* app_data_ptr_ = nullptr; // 应用层共享内存指针
SharedData* sys_data_ptr_ = nullptr; // 内核层共享内存指针
// 应用层不能使用内核层指针!!!
void OutputErr(KSError error, const char* pFuncName, const char* pComment)
{
if (error == KS_OK)
return;
const char* pError;
KS_getErrorString(error, &pError, KSLNG_DEFAULT);
printf("ERROR (%08X = \'%s\') - %s: %s\n", error, pError, pFuncName, pComment);
}
int main()
{
KSError error;
error = KS_openDriver(customerNumber); // 所有Kithar项目的第一步
if (error != KS_OK)
{
error = 1;
OutputErr(error, "KS_openDriver", "Unable to open the driver!");
}
else
{
printf("Hello Kithara! \n");
}
// 指定内核程序执行的CPU
KSSystemInformation systemInfo;
systemInfo.structSize = sizeof(KSSystemInformation);
error = KS_getSystemInformation(&systemInfo, KSF_NO_FLAGS);
if (error != KS_OK)
{
OutputErr(error, "KS_getSystemInformation", "");
KS_closeDriver();
return 0;
}
int cpu = systemInfo.numberOfCPUs - 1;
error = KS_setTargetProcessor(cpu, KSF_NO_FLAGS);
if (error != KS_OK)
{
OutputErr(error, "KS_setTargetProcessor", "");
KS_closeDriver();
return 0;
}
// 创建共享内存
error = KS_createSharedMem(reinterpret_cast<void**>(&app_data_ptr_), reinterpret_cast<void**>(&sys_data_ptr_), "KitharaDemo", sizeof(SharedData), 0);
if (error != KS_OK)
{
OutputErr(error, "KS_createSharedMem", "failed to allocate shared memory");
KS_closeDriver();
return 0;
}
// 加载内核DLL
KSHandle kermel_handle;
error = KS_loadKernel(&kermel_handle, "KitharaDll.dll", nullptr, nullptr, KSF_KERNEL_EXEC | KSF_SAVE_FPU);
if (error != KS_OK)
{
OutputErr(error, "KS_loadKernel", "load dll failed!");
KS_closeDriver();
return 0;
}
// 初始化内核层
error = KS_execKernelFunction(kermel_handle, "_initFunction", sys_data_ptr_, KS_INVALID_HANDLE, KSF_NO_FLAGS);
if (error != KS_OK)
{
OutputErr(error, "KS_execKernelFunction", "load dll failed!");
KS_closeDriver();
return 0;
}
Sleep(100);
if (app_data_ptr_->timer_handle == NULL)
{
printf("timer_handle is NULL \n");
}
else
{
// 计时器启动
error = KS_startTimer(app_data_ptr_->timer_handle, KSF_NO_FLAGS, 0); // 1ms
if (error != KS_OK)
{
OutputErr(error, "KS_startTimer", "");
KS_closeDriver();
return 0;
}
}
int timer_out = 10;
while (timer_out)
{
if (app_data_ptr_ != nullptr)
{
printf("Timer Count: %d \n", app_data_ptr_->timer_count);
}
Sleep(100);
timer_out--;
}
// 移除计时器
if (app_data_ptr_->timer_handle != NULL)
{
KS_stopTimer(app_data_ptr_->timer_handle);
KS_removeTimer(app_data_ptr_->timer_handle);
}
// 移除回调
if (app_data_ptr_->timer_call_back != NULL)
{
KS_removeCallBack(app_data_ptr_->timer_call_back);
}
// 资源内核
error = KS_freeKernel(kermel_handle);
if (error != KS_OK)
{
OutputErr(error, "KS_freeKernel", "");
KS_closeDriver();
return 0;
}
// 资源共享内存
error = KS_freeSharedMem(app_data_ptr_);
if (error != KS_OK)
{
OutputErr(error, "KS_freeSharedMem", "");
KS_closeDriver();
return 0;
}
// 关闭驱动
error = KS_closeDriver();
if (error != KS_OK)
{
OutputErr(error, "KS_closeDriver", "");
KS_closeDriver();
return 0;
}
Sleep(1000);
return 0;
}
KitharaDll.cpp
#include "SharedData.h"
#include <cstdint>
SharedData* sys_data_ptr_ = nullptr;
// 计时器回调
KSError __stdcall _timerCallBack(void* /*pArgs*/, void* /*pContext*/);
// 初始化函数
extern "C" KSError __declspec(dllexport) __stdcall _initFunction(void* pArgs, void* /*pContext*/) {
sys_data_ptr_ = (SharedData*)pArgs;
if (sys_data_ptr_ == nullptr)
{
return -1;
}
// 计时器回调
KSError error = KS_createCallBack(&sys_data_ptr_->timer_call_back, _timerCallBack, nullptr, KSF_DIRECT_EXEC, 0);
if (error != KS_OK)
{
return error;
}
// 创建计时器
error = KS_createTimer(&sys_data_ptr_->timer_handle, CONTROL_PERIOD * 10, sys_data_ptr_->timer_call_back, KSF_REALTIME_EXEC | KSF_DONT_START);
if (error != KS_OK)
{
return error;
}
return KS_OK;
}
// 计时器回调
KSError __stdcall _timerCallBack(void* /*pArgs*/, void* /*pContext*/) {
if (sys_data_ptr_ != nullptr)
{
sys_data_ptr_->timer_count++;
}
return KS_OK;
}
#define WIN32_LEAN_AND_MEAN
// Windows 头文件
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved) {
return TRUE;
}
提示 : 内核层代码改动,编译应用层代码,可能不会编译内核层代码,可以添加依赖项使其代码保持同步。
示例更多:
smp\EtherCATDataExchange
smp\EtherCATBasics
smp\EtherCATBridgeConfig
smp\RealTimeJitterMeasure
smp\NetworkEthernetSend
...