/****************************************************************************** 版权所有: 文件名称: dido.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2013-02-28 功能说明: 开入开出 其它说明: // 启动继电器 1、启动继电器可以重复多次启动,每次启动都将重新计算超时时间。 2、启动继电器有2种关闭方式: 1) 超时自动关闭,超时时间为装置参数的遥控超时时间。 2) 通过函数调用关闭。 3、有保护启动标志时,选择继电器不允许关闭。 4、需提供启动继电器的超时设置函数. 5、只所以采用超时自动关闭,是因为以前的程序关闭方式存在问题,可能由于资源竞争的关系, 导致不该关闭时却关闭了,虽然概率很小,但理论上存在。 修改记录: */ /*------------------------------- 头文件 -------------------------------------- */ #include "head.h" #ifdef GW_AREA_MAIN_2021 /*------------------------------- 宏定义 -------------------------------------- */ /*------------------------------ 类型结构 ------------------------------------- */ /*------------------------------ 全局变量 ------------------------------------- */ struct di g_di[EQU_SLOT_NUM_MAX]; struct _do g_do[EQU_SLOT_NUM_MAX]; struct di_struct g_di_st[EQU_SLOT_NUM_MAX][DIDO_MAX_DI_PER_SLOT*2]; int g_do_flag; struct rt_stat g_stat_di_delay; struct rt_stat g_stat_do_delay; static u32 di_error_count = 0; static u32 di_change_num = 0; static u32 error_count = 0; struct do_time g_do_time[EQU_SLOT_NUM_MAX]; /*------------------------------ 函数声明 ------------------------------------- */ int dido_do_time(void); /*------------------------------ 外部函数 ------------------------------------- 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性. */ int dido_init(void) { int i; memset(g_di,0,sizeof(g_di)); memset(g_do,0,sizeof(g_do)); memset(g_do_time,0,sizeof(g_do_time)); // 检查装置配置 if(equ_config_null()) { return -1; } // 初始化取反标志 for(i=0; i< g_equ_config->di_num; i++) { if(g_equ_config_di[i].is_Inverse == 0) { continue; } if(g_equ_config_di[i].slot < EQU_SLOT_NUM_MAX && g_equ_config_di[i].index < DIDO_MAX_DI_PER_SLOT) { if(g_equ_config_di[i].index < 32) { g_di[g_equ_config_di[i].slot].inv[0] |= 1<= EQU_SLOT_NUM_MAX || index >= 66) { return 0; } if(index < 32) { if(g_di[slot].value[0]&(1<= EQU_SLOT_NUM_MAX || index >= 66) { return 0; } #if 0 { static int cnt = 0; if(cnt++ < 16) rt_printf("slot=%d,index=%d,ts=%d.\r\n",slot,index,ts); } #endif ts_i.n = g_di[slot].ts_i[index].n - 1; //ts_i下是无值的,所以应该减1 for(i=0;i= 0) { return g_di[slot].ts_v[index][ts_i.n]; } ts_i.n--; } if(index < 32) { if(g_di[slot].value[0]&(1<src >= EQU_SLOT_NUM_MAX) { return -1; } if(cfh->len != 8) { return -2; } // 检查帧类型 switch(buf[0]) { case CAN_FRAME_TYPE_DI_INIT: case CAN_FRAME_TYPE_DI_ON: case CAN_FRAME_TYPE_DI_OFF: j = 0; break; case CAN_FRAME_TYPE_DI_INIT1: case CAN_FRAME_TYPE_DI_ON1: case CAN_FRAME_TYPE_DI_OFF1: j = 1; break; case CAN_FRAME_TYPE_DI_INIT2: case CAN_FRAME_TYPE_DI_ON2: case CAN_FRAME_TYPE_DI_OFF2: j = 2; break; default: return -3; } // 获取数据 memcpy(&di,buf+4,4); switch(buf[0]) { case CAN_FRAME_TYPE_DI_INIT: case CAN_FRAME_TYPE_DI_INIT1: case CAN_FRAME_TYPE_DI_INIT2: { //g_di[cfh->src].value[j] = di^g_di[cfh->src].inv[j]; value_old = g_di[cfh->src].value[j]; value_new = di^g_di[cfh->src].inv[j]; di = value_old ^ value_new; // 得到有变化的位 g_di[cfh->src].value[j] = value_new; // rt_printf("DI_INIT:src=%d,j=%d,di=%08x,oldv=%08x,newv=%08x.\r\n",cfh->src,j,di,value_old,value_new); if(g_di[cfh->src].bInited[j] == 0) { // 更新逻辑模型中的值 for(i=0;isrc].value[j]&(1<src][(j<<5)+i].owner, g_di_st[cfh->src][(j<<5)+i].type, is_on ? SW_DI_TYPE_ON : SW_DI_TYPE_OFF); // 双点的值也需要重新初始化 plc_db_init_value(cfh->src,(j<<5)+i,is_on); // 遥信时标 g_di[cfh->src].ts_t[i][g_di[cfh->src].ts_i[i].n] = 0; g_di[cfh->src].ts_v[i][g_di[cfh->src].ts_i[i].n] = is_on; g_di[cfh->src].ts_i[i].n++; } g_di[cfh->src].bInited[j] = true; // bDI_Init[cfh->src] = true; return 0; } } break; case CAN_FRAME_TYPE_DI_ON: case CAN_FRAME_TYPE_DI_ON1: case CAN_FRAME_TYPE_DI_ON2: value_old = g_di[cfh->src].value[j] & (~di); value_new = (di^g_di[cfh->src].inv[j]) & di; g_di[cfh->src].value[j] = value_old | value_new; break; case CAN_FRAME_TYPE_DI_OFF: case CAN_FRAME_TYPE_DI_OFF1: case CAN_FRAME_TYPE_DI_OFF2: value_old = g_di[cfh->src].value[j] & (~di); value_new = ((~di)^g_di[cfh->src].inv[j]) & di; g_di[cfh->src].value[j] = value_old | value_new; break; default: return -3; } // 得到当前时刻,通过关中断保证时间的一致性 rt_irq_save(flags); // gps_get_time(&ts); //modify by EWen can app 对时用的是clk_time_get,此处记录di时间取同一时间源 clk_time_get(&ts); dt=g_adc_dots_count; rt_irq_restore(flags); // 计算变位的真正时刻 // 记录秒的低8位和纳秒的高24位合成32位时间戳放在buf[8]~buf[11]的位置。 // 因为开入滤波时间最长为65.536S,所以用秒的低8位(256S) // 作为时间戳有足够的余量保证时间戳的正确性,但需要考虑秒的进位。 ts_rec.tv_sec = (ts.tv_sec & ~0xff) | buf[8]; ts_rec.tv_nsec=(buf[9]<<24) | (buf[10]<<16) | (buf[11]<<8); if((u8)(ts.tv_sec & 0xff) < buf[8]) { // 如果当前时刻秒的低8位小于记录时刻的低8位,说明当前秒的低8位 // 已进位,记录秒需去掉此进位。 ts_rec.tv_sec -= 0x100; } { u32 diff; diff = ts.tv_nsec + (ts.tv_sec - ts_rec.tv_sec)*NSEC_PER_SEC; diff -= ts_rec.tv_nsec; // 修正对应采样时间点 dt -= (u32)rt_round(g_freq*CFG_ADC_DOTS_PER_PERIOD*diff/NSEC_PER_SEC); rt_stat_in(&g_stat_di_delay,diff); /* di_change_num ++; rt_printf("开入变位:src=%d,buf[0]=%02x,DI==0x%08x,new=0x%08x,T=%d,di_change_num=%d.\r\n", cfh->src, buf[0], g_di[cfh->src].value, di, diff, di_change_num); */ #if 0 if(diff > 1000000) { struct rtc_time_t tm; di_error_count ++; rt_printf("开入变位时间超出次数:%d\r\n\r\n",di_error_count); timespec_to_rtc(ts,&tm,0); rt_printf("当前时间: %04d-%02d-%02d %02d:%02d:%02d:%09d!\r\n", tm.year + 2000, tm.month, tm.day, tm.hour, tm.min, tm.ms/1000, ts.tv_nsec ); timespec_to_rtc(ts_rec,&tm,0); rt_printf("变位时间: %04d-%02d-%02d %02d:%02d:%02d:%09d!\r\n", tm.year + 2000, tm.month, tm.day, tm.hour, tm.min, tm.ms/1000, ts_rec.tv_nsec ); } #endif } // if (bDI_Init[cfh->src]) // { // bDI_Init[cfh->src] = false; // } // else { // 记录遥信变位 for(i=0; i<32; i++) { if(di&(0x01<src].value[j] & (1<src<<8)),is_on,&ts_rec); // 更新逻辑模型中的值 sw_di_set( g_di_st[cfh->src][(j<<5)+i].owner, g_di_st[cfh->src][(j<<5)+i].type, (is_on)? SW_DI_TYPE_ON : SW_DI_TYPE_OFF); // 更新记录 g_di[cfh->src].ts_t[i][g_di[cfh->src].ts_i[i].n] = dt; g_di[cfh->src].ts_v[i][g_di[cfh->src].ts_i[i].n] = is_on; g_di[cfh->src].ts_i[i].n++; } } } return 0; } void dido_qd_set_keeptime(u32 us) { } int dido_do_have_select(u32 slot,u32 index) { // 只有开出板有选择继电器,其它板如辅助板没有 if(g_board_info[slot].type == BOARD_TYPE_DO || g_board_info[slot].type == BOARD_TYPE_DO_2) { return 1; } return 0; } int dido_do(u32 slot,u32 v,int is_on) { unsigned long flags; //检查子板是否正常 if(equ_slot_check(slot) != 0) { return -1; } // 检查DO资源 if(equ_get_do_num(slot) == 0) { return -2; } // 保存输出值,在5ms中断中输出 rt_irq_save(flags); g_do_flag = 1; if(is_on) { g_do[slot].on |= v; } else { g_do[slot].off |= v; } rt_irq_restore(flags); return 0; } int _dido_do(u32 slot,u32 v,int is_on) { int i,ret,err; u8 *buf; u32 do_time; struct timespec ts; // 帧头 buf = can_request_tx_buf(CAN_FRAME_TYPE_DO_ON); if(is_on) { buf[0] = CAN_FRAME_TYPE_DO_ON; } else { buf[0] = CAN_FRAME_TYPE_DO_OFF; } buf[1] = slot; buf[2] = 0; buf[3] = 8; // 开出值 buf[4] = (v>>24) & 0xff; buf[5] = (v>>16) & 0xff; buf[6] = (v>> 8) & 0xff; buf[7] = (v>> 0) & 0xff; // 开出时间 clk_time_get(&ts); do_time = (u32)(((ts.tv_sec&0xff)<<24) | (ts.tv_nsec>>8)); buf[8] = (unsigned char)(do_time>>24); buf[9] = (unsigned char)(do_time>>16); buf[10] = (unsigned char)(do_time>>8); buf[11] = (unsigned char)(do_time); // 开出帧发送,为了提高可靠性,连发2帧 err = 0; for(i=0; i<2; i++) { ret = can_send(equ_slot_can_bus(slot),buf); if(ret < 0) { rt_printf("dido_do err:i=%d,ret=%d\r\n",i,ret); err++; } } return -err; } void dido_do_poll(void) { int i; // 检查开出标志 if(g_do_flag == 0) { return; } g_do_flag = 0; // 真正开出 for(i=0; i (2000*USTIMER_MS)) // if(ustimer_get_duration(us_begin) > (10000*USTIMER_MS)) { break; } } // 关闭所有遥信灯 for(j=0; j= 33) //开入小于32个 //开入大于32个 { if (!(g_di[slot_di].value[1] & (1<<(test_do[j+bd_di60*i*do_num][k]-33)))) //对应开入无值 { do_errflag--; di_ok[k] = test_do[j+bd_di60*i*do_num][k]; } } } if (!do_errflag) //开出对应的所有开入都没有值,即代表该开出有问题 { if (j<8) { dido_buf[i+i*13+1] |= (1<di_num; i++) { if(g_equ_config_di[i].is_Inverse == 0) { continue; } if(g_equ_config_di[i].slot < EQU_SLOT_NUM_MAX && g_equ_config_di[i].index < DIDO_MAX_DI_PER_SLOT) { if(g_equ_config_di[i].index < 32) { g_di[g_equ_config_di[i].slot].inv[0] |= 1< g_do_st[i].us_keep) && (g_do_st[i].us_keep>0)) { dido_do_kz(0,i); } } } } /****************************************************************************** 函数名称: dido_do_poll 函数版本: 01.01 创建作者: 创建日期: 2013-03-13 函数说明: 是否开出巡检 参数说明: 无 返回值: 无 修改记录: */ void dido_do_poll(void) { #if 1 int i; // 真正开出 if(g_do_flag) { g_do_flag = 0; for(i=0; idSDYX_T && g_run_stu.dcjlsd_t) { return; } us0 = ustimer_get_origin(); // 主板,19个遥信 slot = EQU_SLOT_KZ; di_num = equ_get_di_num(slot); // di = gpio_get_di(); di = change_di_ch(~sample_data->yx_buf[0]); rt_printf("gpio_get_di:yx_buf[0]=%x di=%x\r\n",sample_data->yx_buf[0],di); _get_di(slot, di_num, di, sample_data->ts); rt_stat_other_in(6,ustimer_get_duration(us0)); } /****************************************************************************** 函数名称: dido_di_is_on 函数版本: 01.01 创建作者: 创建日期: 2013-03-13 函数说明: 开入状态 参数说明: 无 返回值: 1:有开入,0:无开入 修改记录: */ int dido_di_is_on(u8 slot,u8 index) { if(slot >= EQU_SLOT_NUM_MAX || index >= 64) { return 0; } if(index < 32) { if(g_di[slot].value[0]&(1<= EQU_SLOT_NUM_MAX || index >= 32) { return 0; } #if 0 { static int cnt = 0; if(cnt++ < 16) rt_printf("slot=%d,index=%d,ts=%d.\r\n",slot,index,ts); } #endif ts_i.n = g_di[slot].ts_i[index].n - 1; //ts_i下是无值的,所以应该减1 for(i=0;i= 0) { return g_di[slot].ts_v[index][ts_i.n]; } ts_i.n--; } if(g_di[slot].value[0]&(1<= DO_OUT0) && (i <= DO_OUT7)) { gpio_kout_do(on, i-DO_OUT0); // 对应X4端子的DO0~DO7 _io_do_status_update(on,i); } else { rt_printf("dido_do_kz err(on=%d,i=%d).\r\n",on,i); } return 0; } /*------------------------------ 内部函数 ------------------------------------- 内部函数以下划线‘_’开头,不需要检查参数的合法性. */ static inline void _io_do_status_update(int on, unsigned int i) { if(on) { g_do_status |= (1<>i) & 0x1)) { dido_do_kz(is_on,equ_get_do_channel(i)); } } if(((v>>g_board_info[slot].do_num) & 0x1)) // 启动继电器 { dido_do_kz(is_on,DO_QD_KC); } return 0; } /****************************************************************************** 函数名称: _get_di 函数版本: 01.01 创建作者: 创建日期: 2013-03-13 函数说明: 开入防抖处理 参数说明: 无 返回值: 修改记录: */ #ifdef CN_AREA_GUANGXI//遥信全部为0 u32 kktime_0=0; #endif static int _get_di(int slot,u16 di_num, u32 di, struct timespec ts) { int i,b_on; struct rtc_time_t ct; uint32 diff_nsec; // struct timespec ts; #if 0//#ifdef CN_AREA_GUANGXI//遥信全部为0,则关闭屏幕或者关机 if(di==0) { kktime_0++; if(kktime_0 > 10) { #if 1 g_run_stu.yxError=true; //遥信错误,关闭屏幕 #else char *argv[] = {"poweroff", NULL}; char *envp[] = { NULL }; call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);//关机 #endif return 0; } } else { g_run_stu.yxError=0; kktime_0=0; } #endif // 获取遥信值,并记录时间戳。 // 用秒的低8位和纳秒的高24位合成32位时间戳 // clk_time_get(&ts); // 循环处理每一个遥信 for(i=0; ib_on) { if(!pt->b_first_change) { pt->b_first_change=true; pt->tm_back=0; pt->tm_keep=0; // 记录变位的起始时刻点 pt->ts_di = ts; pt->dt=g_adc_dots_data_count; #ifdef DIDO_TS_MODE pt->ts_tmp = ts; #endif } else { // 累计保持时间 #ifdef DIDO_TS_MODE if(ts.tv_nsec >= pt->ts_tmp.tv_nsec) { diff_nsec = ts.tv_nsec - pt->ts_tmp.tv_nsec; } else { diff_nsec = 1000000000ul - pt->ts_tmp.tv_nsec + ts.tv_nsec; } if(diff_nsec > 312500ul) { diff_nsec = 156250ul; } pt->acc_nsec += diff_nsec; while(pt->acc_nsec >= 156250ul) { pt->tm_keep++; pt->acc_nsec -= 156250ul; } pt->ts_tmp = ts; // rt_printf("diff_nsec:%d, pt->tm_keep:%d\r\n", diff_nsec, pt->tm_keep); #else pt->tm_keep++; #endif pt->tm_back=0; // 现在返回门限是0.5us,所有必须在此清零。 // 保持时间到,处理变位 if(pt->tm_keep > pt->tm_filter) { // 记录遥信状态 #if defined CN_AREA_GUANGXI || defined YX_DI_ERROR //广西遥信异常后,遥信屏蔽不保存,不上报 if( g_run_stu.yxEnable==0 && b_on==0) { pt->type = SOE_TYPE_TST; pt->b_first_change=false; pt->tm_back=0; pt->tm_keep=0; return 0; } #endif pt->b_on = b_on; if(g_di[slot].inv[0] & (1<ts_di); timespec_to_rtc(ts, &ct, 1); rt_printf("di=%x value[0]=%x\r\n",di,g_di[slot].value[0]); // 更新逻辑模型中的值 sw_di_set(g_di_st[slot][i].owner,g_di_st[slot][i].type,b_on ? SW_DI_TYPE_ON : SW_DI_TYPE_OFF); // 更新记录 g_di[slot].ts_t[i][g_di[slot].ts_i[i].n] = pt->dt; g_di[slot].ts_v[i][g_di[slot].ts_i[i].n] = b_on; g_di[slot].ts_i[i].n++; //滤波处理数据复位 pt->b_first_change=false; pt->tm_back=0; pt->tm_keep=0; } } } else if(pt->b_first_change) { // 变位返回处理 pt->tm_back++; // 屏蔽下一行, // 最新逻辑,有效电平不连续可计数累加; // 无效电平连续超0.5ms复归,且不连续累加。 //pt->tm_keep=0; // if(pt->tm_back > pt->tm_filter) if(pt->tm_back>4) // 针对高频振荡波测试优化,返回门限设置为500us附件(4*156=624us)。 { pt->tm_back = 0; pt->tm_keep=0; pt->b_first_change = false; } } } return 0; } /*------------------------------ 测试函数 ------------------------------------- 一个实体文件必须带一个本模块的测试函数来进行单元测试,如果的确不方便在本模块中 进行单元测试,必须在此注明实际的测试位置(例如在哪个实体文件中使用哪个测试函数). */ // SOE信号发生器控制全局变量 extern int g_soe_gen_state; // 状态,0停止,1初始化,2运行 extern int g_soe_gen_on_us; // 导通时间 extern int g_soe_gen_off_us; // 断开时间 extern int g_soe_gen_seq_us; // 通道序列间隔时间 #define SOE_GEN_NUM 8 u32 g_soe_gen_count; u32 g_soe_gen_us0[SOE_GEN_NUM]; u8 g_soe_gen_on[SOE_GEN_NUM]; void dido_soe_gen(void) { int i; if(g_soe_gen_state == 0) { return; } else if(g_soe_gen_state == 1) { pit_156us_period_set(78.125); g_soe_gen_count = 0; for(i=0; i= g_soe_gen_off_us) { g_soe_gen_on[i] = 1; gpio_kout_do(1,i); g_soe_gen_us0[i] = g_soe_gen_count; } } else { if((int)(g_soe_gen_count - g_soe_gen_us0[i]) >= g_soe_gen_on_us) { g_soe_gen_on[i] = 0; gpio_kout_do(0,i); g_soe_gen_us0[i] = g_soe_gen_count; } } } } else { g_soe_gen_count = 0; for(i=0; idi_num; i++) { if((g_equ_config_di[i].slot==1)&&(g_equ_config_di[i].index==index)) { if(g_equ_config_di[i].type==0)return true; } } return false; } bool check_board_kc6_close(u8 slot,u8 index) { u32 i; // 初始化取反标志 index-=1; for(i=0; i< g_equ_config->do_num; i++) { if((g_equ_config_do[i].slot==slot)&&(g_equ_config_do[i].index==index)) { if(g_equ_config_do[i].type!=0)return true; } } return false; } #define DIGROUP 4 #define V3_OUT_NUM 5 int dido_auto_test_for_v3(char *buf,int num) { // 12个开入,用5个开出进行测试 static char test_di[6][DIGROUP] = { {1, 6, 11,0}, {2, 7, 12,0}, {3, 8, 13,0}, {4, 9, 14,0}, {16, 0, 0,0}, {5, 10, 15,0}, }; int i,j,k,err_once,flag,flag1,di_num, do_num; u32 slot_di,slot_do, di_result; u8 tmp[5],tmp_data[5]; u8 di_ok[4] = {0}; u8 do_errflag = 0; char dido_test_pair[num][2]; unsigned long us_begin = 0; bool bkc6close=false; slot_do=0; bkc6close=check_board_kc6_close(1,6); dido_stat_reset(); if(bkc6close) { dido_do(slot_do,(1<<5),1); // 开出6若焊接为常闭输出,先开出,打开节点 ustimer_delay(100*USTIMER_MS); } // 取出板卡槽位 for(i=0;i<(num*2);) { for(j=0;j<2;j++) { dido_test_pair[i/2][j] = buf[i]; i++; } } flag = 0; flag1 = 0; memset(tmp,0,sizeof(tmp)); memset(tmp_data,0,sizeof(tmp_data)); memset(dido_buf,0,sizeof(dido_buf)); rt_printf("dido_test begin...\r\n"); err_once = 0; // 以组为循环 for(i=0; ido_num = %lu\r\n ", g_equ_config->do_num); for(i=0; i<(int)sizeof(brd_v3); i++) { if(g_brd_type_kz+BOARD_TYPE_DFTU_KC00==brd_v3[i])// { rt_printf("auto_test_for_v3\r\n "); return (dido_auto_test_for_v3(buf, num)); } } rt_printf("auto_test_for_v4\r\n "); //bkc6close=check_board_kc6_close(1,6); dido_stat_reset(); if(bkc6close) { dido_do(1,(1<<5),1); // 开出6若焊接为常闭输出,先开出,打开节点 ustimer_delay(100*USTIMER_MS); } // 取出板卡槽位 for(i=0;i<(num*2);) { for(j=0;j<2;j++) { dido_test_pair[i/2][j] = buf[i]; i++; } } flag = 0; flag1 = 0; memset(tmp,0,sizeof(tmp)); memset(tmp_data,0,sizeof(tmp_data)); memset(dido_buf,0,sizeof(dido_buf)); rt_printf("dido_test begin...\r\n"); err_once = 0; // 以组为循环 for(i=0; i