基于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
相关推荐
茶猫_9 分钟前
力扣面试题 39 - 三步问题 C语言解法
c语言·数据结构·算法·leetcode·职场和发展
初学者丶一起加油11 分钟前
C语言基础:指针(数组指针与指针数组)
linux·c语言·开发语言·数据结构·c++·算法·visual studio
半盏茶香2 小时前
C语言勘破之路-最终篇 —— 预处理(上)
c语言·开发语言·数据结构·c++·算法
2401_858286112 小时前
118.【C语言】数据结构之排序(堆排序和冒泡排序)
c语言·数据结构·算法
Zer0_on10 小时前
数据结构栈和队列
c语言·开发语言·数据结构
一只小bit10 小时前
数据结构之栈,队列,树
c语言·开发语言·数据结构·c++
马浩同学10 小时前
【GD32】从零开始学GD32单片机 | DAC数模转换器 + 三角波输出例程
c语言·单片机·嵌入式硬件·mcu
一个没有本领的人11 小时前
win11+matlab2021a配置C-COT
c语言·开发语言·matlab·目标跟踪
一只自律的鸡11 小时前
C项目 天天酷跑(下篇)
c语言·开发语言
长安——归故李11 小时前
【C语言】成绩等级制
c语言·开发语言