基于asynPortDriver的asyn异步驱动程序示例

这个示例驱动程序是能找到的使用asynPortDriver类的最简单示例程序,它的参数库中仅含有两个参数,一个参数索引为P_Value,,只读,另一个参数索引为P_Range, 可读写。这些参数对应的字符串"VALUE"和"RANGE"是记录实例中INP/OUT字段中asyn接口参数查找对应索引需要使用的。这个程序中单独使用了一个线程运行asynDemo::update_code_wrapper函数,这个函数使用的参数是epicsThreadCreate调用中,传递给它的,是一个asynDemo实例的地址,在这个函数中运行asynDemo::update_code()函数。

1、asynDemo.c源代码如下:

cpp 复制代码
/*
 *   asynTest.cpp:带后台线程,一个更新参数,一个配置参数
 */

#include <iocsh.h>
#include <epicsThread.h>
#include <asynPortDriver.h>

#include <epicsExport.h>

/*
    参数名称:用参数类型记下支持的参数是自定义的并且此种方式在一个地方访问。
 */
#define P_ValueString         "VALUE"         /* asynFloat64,  r/o */
#define P_RangeString         "RANGE"         /* asynFloat64,  r/w */


/** 建议asynPortDriver是用于大多数基于asyn的硬件访问的基类  */
class asynDemo : public asynPortDriver
{
public:
    asynDemo(const char *portName);

private:
    // 在asyn内用于识别一个端口参数的索引
    int P_Value;
    int P_Range;

    // 进行实际工作的后台线程
    void update_code();

    static void update_code_wrapper(void *arg);
};


/**  传入参数portName:要被创建的asyn端口驱动的名称  */
asynDemo::asynDemo(const char *portName)
   : asynPortDriver(portName,
                    1, // 这个端口上支持多少地址
                    asynFloat64Mask | asynDrvUserMask, // 直接的接口(类型)
                    asynFloat64Mask,  // 中断掩码, 支持I/O Intr的类型
                    0, // ASYN_.. 标志.此驱动不阻塞并且它不是多设备的
                    1, // 自动连接
                    0, //  默认优先级
                    0) //  默认栈尺寸
{
    // 为这个端口注册一个或多个参数
    createParam(P_ValueString, asynParamFloat64, &P_Value);
    createParam(P_RangeString, asynParamFloat64, &P_Range);

    // 设置初始值
    setDoubleParam(P_Value,  0.0);
    setDoubleParam(P_Range,  5.0);

    // 创建执行值更新的线程
    epicsThreadCreate("asynDemoTask",
                      epicsThreadPriorityMedium,
                      epicsThreadGetStackSize(epicsThreadStackMedium),
                      update_code_wrapper, this);// 指定启动线程运行的代码是这个类的成员函数update_code_wrapper
                      //其传递的参数是this,指向此类的本实例对象
}

/*  epicsThreadCreate()需要一个C函数,
 *  因而传递这个这个函数,其调用所要的C++方法
 *  epicsThreadCreate()调用这个方法时,将最后一个参数传给这个方法
 *  在本方法中,转换成获取到这个类的实例,并且调用其update_code()方法
 */
void asynDemo::update_code_wrapper(void *arg)
{
    asynDemo *self = (asynDemo *)arg;
    self->update_code();
}

void asynDemo::update_code()
{
    double value, range;

    while (1)
    {
        // 当与asyn参数库交换时锁定:从参数库获取索引为P_Value和P_Range为值
        lock();
        getDoubleParam(P_Value, &value);
        getDoubleParam(P_Range, &range);
        unlock();

        // 在忙于做别的事情时,解锁。
        value += 1;
        // 模拟这花费很长时间
        epicsThreadSleep(2.0);
        if (value > range)
            value = 0.0;

        // 为asyn调用再次锁定
        lock();
        updateTimeStamp();
        // 设置参数库中索引为P_Value的值
        setDoubleParam(P_Value, value);
        callParamCallbacks();
        unlock();
    }
}


/*
 * 在IOC shell中注册'asynDemoConfigure(portName)'
 */
extern "C"
{
// 定义每个参数格式:参数名和参数类型
static const iocshArg initArg0 = { "portName", iocshArgString };
// 定义参数数组
static const iocshArg * const initArgs[] = { &initArg0 };
// 定义函数:函数名,参数数目,参数数组
static const iocshFuncDef initFuncDef = {"asynDemoConfigure", 1, initArgs };
// 函数的参数调用
static void initCallFunc(const iocshArgBuf *args)
{
    new asynDemo(args[0].sval);
}
// 注册函数
void asynDemoRegister(void)
{
    iocshRegister(&initFuncDef, initCallFunc);
}

// asynDemoSupport.dbd
epicsExportRegistrar(asynDemoRegister);
}

2、使用的数据库文件为:

bash 复制代码
# ai输入记录通过DTYP字段和INP字段查找到asyn异步驱动程序中索引为P_Value的参数
record(ai, "$(P)$(R)Value")
{
   field(PINI, "1")
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))VALUE")
   field(PREC, "5")
   field(SCAN, "I/O Intr")
}

# ao输出记录通过DTYP字段和OUT字段查找到asyn异步驱动程序中索引为P_Range的参数
record(ao, "$(P)$(R)Range")
{
   field(PINI, "1")
   field(DTYP, "asynFloat64")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))RANGE")
   field(PREC, "5")
}

3、修改与以上文件处于相同目录中的Makefile文件,指定需要编译以上源文件,返回这个IOC的顶层目录执行make。

4、修改启动目录中st.cmd启动文件:

bash 复制代码
...

asynDemo_registerRecordDeviceDriver pdbbase

asynDemoConfigure("dev1")

## Load record instances
dbLoadRecords("db/asynDemo.db","P=TEST:,R=ASYNDEMO:,PORT=dev1,ADDR=0,TIMEOUT=1")

cd "${TOP}/iocBoot/${IOC}"
iocInit

5、启动以上ioc程序,查看当前IOC中加载的记录实例:

bash 复制代码
epics> dbl
TEST:ASYNDEMO:Value
TEST:ASYNDEMO:Range

6、通过通道访问测试以上PV:

PV:TEST:ASYNDEMO:Range限制PV:TEST:ASYNDEMO:Value的最大值,asyn异步驱动程序每2秒种将TEST:ASYNDEMO:Value对应的参数库值加1,当其大于TEST:ASYNDEMO:Range对那个的参数库值时,对其置0。

bash 复制代码
orangepi@orangepi4-lts:~$ caget TEST:ASYNDEMO:Range
TEST:ASYNDEMO:Range            5
orangepi@orangepi4-lts:~$ camonitor TEST:ASYNDEMO:Value
TEST:ASYNDEMO:Value            2023-08-12 08:41:58.424114 1
TEST:ASYNDEMO:Value            2023-08-12 08:42:00.424513 2
TEST:ASYNDEMO:Value            2023-08-12 08:42:02.424904 3
TEST:ASYNDEMO:Value            2023-08-12 08:42:04.425315 4
TEST:ASYNDEMO:Value            2023-08-12 08:42:06.425706 5
TEST:ASYNDEMO:Value            2023-08-12 08:42:08.426095 0
TEST:ASYNDEMO:Value            2023-08-12 08:42:10.426491 1
^C
orangepi@orangepi4-lts:~$ caput TEST:ASYNDEMO:Range 2
Old : TEST:ASYNDEMO:Range            5
New : TEST:ASYNDEMO:Range            2
orangepi@orangepi4-lts:~$ camonitor TEST:ASYNDEMO:Value
TEST:ASYNDEMO:Value            2023-08-12 08:42:32.430502 0
TEST:ASYNDEMO:Value            2023-08-12 08:42:34.430941 1
TEST:ASYNDEMO:Value            2023-08-12 08:42:36.431285 2
TEST:ASYNDEMO:Value            2023-08-12 08:42:38.431546 0
^C
相关推荐
jiao0000135 分钟前
数据结构——队列
c语言·数据结构·算法
铁匠匠匠39 分钟前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计
C-SDN花园GGbond41 分钟前
【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)
c语言·开发语言·数据结构·排序算法
Navigator_Z3 小时前
数据结构C //线性表(链表)ADT结构及相关函数
c语言·数据结构·算法·链表
菜菜想进步3 小时前
内存管理(C++版)
c语言·开发语言·c++
知星小度S3 小时前
C语言——自定义类型
c语言·开发语言
cleveryuoyuo4 小时前
二叉树的链式结构和递归程序的递归流程图
c语言·数据结构·流程图
科研小白_d.s4 小时前
vscode配置c/c++环境
c语言·c++·vscode
暮色_年华5 小时前
嵌入式C语言自我修养:C语言的模块化的编程思想
c语言
大母猴啃编程6 小时前
数据结构---非线性--树
c语言·数据结构·学习·算法·青少年编程