/****************************************************************************** 版权所有: 文件名称: dido.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2013-02-28 功能说明: 开入开出 其它说明: // 启动继电器 1、启动继电器可以重复多次启动,每次启动都将重新计算超时时间。 2、启动继电器有2种关闭方式: 1) 超时自动关闭,超时时间为装置参数的遥控超时时间。 2) 通过函数调用关闭。 3、有保护启动标志时,选择继电器不允许关闭。 4、需提供启动继电器的超时设置函数. 5、只所以采用超时自动关闭,是因为以前的程序关闭方式存在问题,可能由于资源竞争的关系, 导致不该关闭时却关闭了,虽然概率很小,但理论上存在。 修改记录: */ /*------------------------------- 头文件 -------------------------------------- */ #include "head.h" #include "gpio.h" #include "ad7616.h" /*------------------------------- 宏定义 -------------------------------------- */ #define DO_QD_KC DO_OUT_PRESET /*------------------------------ 类型结构 ------------------------------------- */ /*------------------------------ 全局变量 ------------------------------------- */ extern u32 g_brd_type_kz; u16 g_do_status; // 开出状态,因为仅主板有开出 u32 g_do_start_time; int g_do_flag; u8 g_di_slot = 0;// 开入板卡配置槽位 u8 g_do_slot = 0;// 开入板卡配置槽位 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]; struct do_struct g_do_st[DO_NUM]; struct rt_stat g_stat_di_delay; struct rt_stat g_stat_do_delay; static u32 di_error_count = 0; static u32 error_count = 0; struct do_time g_do_time[EQU_SLOT_NUM_MAX]; /*------------------------------ 内部函数 ------------------------------------- 内部函数以下划线‘_’开头,不需要检查参数的合法性. */ /** * @brief 开出状态更新 * @param[in/out] {int} on * @param[in/out] {unsigned int} i * @return * void * @retval none * * @warning none * @note none */ static inline void _io_do_status_update(int on, unsigned int i) { if (on) { g_do_status |= (1 << i); g_do_st[i].us_on = ustimer_get_origin(); } else { g_do_status &= ~(1 << i); } // 更新返校检查时间 g_do_start_time = ustimer_get_origin(); } /** * @brief 控制开出动作 * @param[in/out] {u32} slot 板卡索引 * @param[in/out] {u16} v 控制码 * @param[in/out] {int} is_on 合分 * @return * int * @retval none * * @warning none * @note none */ static int _dido_do(u32 slot, u16 v, int is_on) { int i, l_ret = -1; // 循环检查开出位 for (i = 0; i < g_board_info[slot].do_num; i++) { // 如果对应开出位有效,开出。 if (((v >> i) & 0x1)) { l_ret = dido_do_kz(is_on, equ_get_do_channel(i)); } } return l_ret; } #if !defined CPU_FUXI /** * @brief 开入防抖处理 * @param[in/out] {type} slot * @param[in/out] {type} di_num * @param[in/out] {type} di * @param[in/out] {type} ts * @return * int * @retval none * * @warning none * @note none */ 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; // 获取遥信值,并记录时间戳。 // 用秒的低8位和纳秒的高24位合成32位时间戳 // clk_time_get(&ts); // 循环处理每一个遥信 for (i = 0; i < di_num; i++) { struct di_struct *pt = &g_di_st[slot][i]; // 处理当前变位遥信。 b_on = (di & (1 << i)) ? 1 : 0; if (b_on ^ pt->b_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 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 << i)) { b_on = !b_on; } if (b_on) { g_di[slot].value[0] |= (1 << i); } else { g_di[slot].value[0] &= ~(1 << i); } // 记录SOE soe_record_yx((WORD)(i + (slot << 8)), b_on, &pt->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; } #endif /*------------------------------ 外部函数 ------------------------------------- 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性. */ /** * @brief 开入开出初始化 * @return * int * @retval none * * @warning none * @note none */ int dido_init(void) { u32 i; // 检查装置配置 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 << g_equ_config_di[i].index; } else { g_di[g_equ_config_di[i].slot].inv[1] |= 1 << (g_equ_config_di[i].index - 32); } } } dido_stat_reset(); return 0; } /** * @brief 判断是否需要动作预置继电器 * @param[in/out] {u32} slot * @param[in/out] {u32} index * @return * int * @retval none * * @warning none * @note none */ int dido_do_have_select(u32 slot, u32 index) { int i = 0; i = equ_get_do_channel(index); if (i == DO_OUT0 || i == DO_OUT1 || i == DO_OUT2 || i == DO_OUT3) { return 1; } return 0; } /****************************************************************************** 函数名称: dido_do 函数版本: 01.01 创建作者: 创建日期: 2013-03-13 函数说明: 板卡开出动作 参数说明: 无 返回值: 修改记录: */ int dido_do(u32 slot, u16 v, int is_on) { // unsigned long flags; // 检查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; } /****************************************************************************** 函数名称: do_kout_check 函数版本: 创建作者: 创建日期: 2013-03-13 函数说明: 开出反校。 参数说明: 无 返回值: 结果 修改记录: */ void io_do_check(void) { u16 now, fj; u32 us; #ifdef DO_KOUT_CHECK u8 index; // 取返校值,必须在下面时间点检查之 index = g_equ_config_di[g_sw[0].di_cfg_index[SW_DI_KOCHK]].index; fj = gpio_di_fj(index) & 0x01; // 0x02; // 返回值只判断启动、分闸、合闸 now = 0; if (g_do_status & (1 << DO_QD_KC)) // 预置反校DO_PWM_SW_QD { now |= 0x01; } #endif // 开出动作20ms内不进行开出自检, // 经实测启动继电器因为是继电器触点返校,其返回时间最长,但也小于10ms。 // 分合闸继电器是光耦返校,本身需要的时间可以忽略不计,但受启动继电器的影响 us = ustimer_get_duration(g_do_start_time); if (us < 20 * 1000) { return; } if (fj != now) { // rt_printf("返校失败:fj=%04x,now=%04x, g_do_status=%04x(us=%d)\r\n",fj,now, g_do_status,us); rt_err_set(ERR_CODE_DO_CHECK, 0); return; } } /****************************************************************************** 函数名称: io_do_return 函数版本: 01.01 创建作者: 创建日期: 2013-03-13 函数说明: 开出保持时间 参数说明: 无 返回值: 无 修改记录: */ void io_do_return(void) { int i; u32 dly; for (i = 0; i < DO_NUM; i++) // 开出保持 { if (g_do_status & (1 << i)) // 正在开出 { dly = ustimer_get_duration(g_do_st[i].us_on); // +2500是为了提高脉宽精度,因为此函数是在5ms(5000)中断中调用的. // 如果us_keep为0,表示此开出没有自动返回的需求,应忽略。 if (((dly + 2500) > 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) { int i; // 真正开出 if (g_do_flag) { g_do_flag = 0; for (i = 0; i < EQU_SLOT_NUM_MAX; i++) { if (g_do[i].on) { _dido_do(i, g_do[i].on, 1); g_do[i].on = 0; } if (g_do[i].off) { _dido_do(i, g_do[i].off, 0); g_do[i].off = 0; } } } // 开出返回 io_do_return(); #if !defined CPU_FUXI // 开出返校 if (g_board_info[EQU_SLOT_KZ].do_num != 0) { #ifdef DO_KOUT_CHECK if ((short)g_sw[0].di_cfg_index[SW_DI_KOCHK] != INDEX_INVALLID) { io_do_check(); } #else // io_do_check(); #endif } #endif return; } /****************************************************************************** 函数名称: dido_qd_set_keeptime 函数版本: 01.01 创建作者: sunxi 创建日期: 2013-06-10 函数说明: 设置启动继电器的保持时间 参数说明: 无 返回值: 无 修改记录: */ void dido_qd_set_keeptime(u32 us) { g_do_st[DO_QD_KC].us_keep = us; } #if !defined CPU_FUXI /****************************************************************************** 函数名称: get_di 函数版本: 01.01 创建作者: 创建日期: 2013-03-13 函数说明: 开入防抖处理 参数说明: 无 返回值: 无 修改记录: */ // unsigned int di_val; void dido_di_poll(SHM_SAMPLE_T *sample_data) { u32 di, di_num, slot; u64 us0; // 如果交流延时失电信号有,闭锁遥信 if (pRunSet->dSDYX_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)); } #endif /****************************************************************************** 函数名称: 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 << index)) { return 1; } } else { index -= 32; if (g_di[slot].value[1] & (1 << index)) { return 1; } } return 0; } // 查看某个时间点下的开入值 int dido_di_is_on_ts(u8 slot, u8 index, u32 ts) { int i, r; struct ts_index ts_i; if (slot >= 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 < DIDO_TS_NUM; i++) { r = (int)(ts - g_di[slot].ts_t[index][ts_i.n]); if (r >= 0) { return g_di[slot].ts_v[index][ts_i.n]; } ts_i.n--; } if (g_di[slot].value[0] & (1 << index)) { return 1; } return 0; } int dido_do_kz(int on, unsigned int i) { int l_ret = -1; if (on) // 开出,需启动启动继电器打开电源 { if (i == DO_OUT0 || i == DO_OUT1 || i == DO_OUT2 || i == DO_OUT3) { l_ret = gpio_kout_do(on, DO_QD_KC - DO_OUT0); _io_do_status_update(1, DO_QD_KC); } } if (i == DO_QD_KC) // 启动继电器出口 { if (!on) { if (g_tRelay[0].zqd) // 如果保护启动,禁止关闭启动继电器 { return -2; } if (g_do_status & (~(1 << DO_QD_KC))) // 有其他开出,不收启动继电器 { return -3; } } l_ret = gpio_kout_do(on, i - DO_OUT0); // 对应X4端子的DO0~DO7 _io_do_status_update(on, i); } else if ((i >= DO_OUT0) && (i <= DO_OUT7)) { l_ret = gpio_kout_do(on, i - DO_OUT0); // 对应X4端子的DO0~DO7 _io_do_status_update(on, i); } else { dp_err_n_c_rt("dido_do_kz err(on = %d, i = %d).", on, i); } return l_ret; } #if !defined CPU_FUXI /** * @brief 开入量更新 * @param[in/out] {uint32_t} _value_new 值 * @param[in/out] {uint8_t} _index 下标 * @param[in/out] {struct timespec *ts} ts 时间 * @return * int * @retval none * * @warning none * @note none */ int dido_di_update(uint32_t _value_new, uint8_t _index, struct timespec *ts) { } #endif /** * @brief Get the shm didata object * @return * int * @retval none * * @warning none * @note none */ int get_shm_didata(void) { uint8_t b_on = 0; u32 ul_di_state = 0, dt = 0; int l_read_ret = 0, l_cnt = 0; struct t_shmdata_di shm_data_di = {0}; static struct t_sd_di_status smt_di[DI_MAX] = {0}, smt_di_bk[DI_MAX] = {0}; /* 数据 */ struct timespec ts; struct di_struct *pt_di = NULL; l_read_ret = shm_packet_read_v2(SHM_ADDR_W_DI, sizeof(shm_data_di), (uint8_t *)&shm_data_di, sizeof(shm_data_di)); if (l_read_ret < 0) { return -1; } if (l_read_ret > 0) { memcpy((void *)&smt_di, (void *)&shm_data_di.di, sizeof(struct t_sd_di_status) * DI_MAX); /* 第一次 数据初始化 */ if (false == g_di[g_di_slot].bInited) { for (uint8_t i = 0; i < DI_MAX; i++) { pt_di = &g_di_st[g_di_slot][i]; b_on = smt_di[i].uc_state; if (g_di[g_di_slot].inv[0] & (1 << i)) { b_on = !b_on; } pt_di->b_on = b_on; ul_di_state |= b_on << i; sw_di_set(pt_di->owner, pt_di->type, b_on ? SW_DI_TYPE_ON : SW_DI_TYPE_OFF); // 双点的值也需要重新初始化 plc_db_init_value(g_di_slot, i, b_on); // 遥信时标 g_di[g_di_slot].ts_t[i][g_di[g_di_slot].ts_i[i].n] = 0; g_di[g_di_slot].ts_v[i][g_di[g_di_slot].ts_i[i].n] = b_on; g_di[g_di_slot].ts_i[i].n++; } memcpy((void *)&smt_di_bk, (void *)&shm_data_di.di, sizeof(struct t_sd_di_status) * DI_MAX); g_di[g_di_slot].bInited = TRUE; } for (uint8_t i = 0; i < DI_MAX; i++) { pt_di = &g_di_st[g_di_slot][i]; b_on = smt_di[i].uc_state; if (g_di[g_di_slot].inv[0] & (1 << i)) { b_on = !b_on; // dp_err_n_c_rt("b_on = %d", b_on); } pt_di->b_on = b_on; ul_di_state |= b_on << i; // dp_err_n_c_rt("ul_di_state = 0x%04x", ul_di_state); if (smt_di_bk[i].uc_state != smt_di[i].uc_state) { transform_msts_to_tts(smt_di[i].ull_timestamp, &ts); soe_record_yx(i + (g_di_slot << 8), b_on, &ts); // 更新逻辑模型中的值 sw_di_set(pt_di->owner, pt_di->type, (b_on) ? SW_DI_TYPE_ON : SW_DI_TYPE_OFF); // 更新记录 dt = g_adc_dots_data_count; dt = ((dt + ADC_REC_DOTS_MASK) - (pt_di->tm_filter * ADC_REC_SAMPLE)) % ADC_REC_DOTS_MASK; g_di[g_di_slot].ts_t[i][g_di[g_di_slot].ts_i[i].n] = dt; g_di[g_di_slot].ts_v[i][g_di[g_di_slot].ts_i[i].n] = b_on; g_di[g_di_slot].ts_i[i].n++; smt_di_bk[i].uc_state = smt_di[i].uc_state; // dp_err_n_c_rt("change smt_di[%02d].uc_state = %d", i, b_on); } } // dp_err_n_c_rt("inv[0] = %d, value[0] = 0x%04x, ul_di_state = 0x%04x", g_di[g_di_slot].inv[0], g_di[g_di_slot].value[0], ul_di_state); g_di[g_di_slot].value[0] = ul_di_state ^ g_di[g_di_slot].inv[0]; // dp_err_n_c_rt("inv[0] = %d, value[0] = 0x%04x, ul_di_state = 0x%04x", g_di[g_di_slot].inv[0], g_di[g_di_slot].value[0], ul_di_state); } return l_read_ret; } /*------------------------------ 测试函数 ------------------------------------- 一个实体文件必须带一个本模块的测试函数来进行单元测试,如果的确不方便在本模块中 进行单元测试,必须在此注明实际的测试位置(例如在哪个实体文件中使用哪个测试函数). */ // 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 < SOE_GEN_NUM; i++) { g_soe_gen_us0[i] = g_soe_gen_seq_us * (i); g_soe_gen_on[i] = 0; g_soe_gen_state = 2; } } else if (dido_di_is_on(1, 0)) { for (i = 0; i < SOE_GEN_NUM; i++) { if (g_soe_gen_on[i] == 0) { if ((int)(g_soe_gen_count - g_soe_gen_us0[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; i < SOE_GEN_NUM; i++) { g_soe_gen_us0[i] = g_soe_gen_seq_us * (i); g_soe_gen_on[i] = 0; g_soe_gen_state = 2; } } g_soe_gen_count++; } int dido_printf(void) { int i, slot, index; struct ts_index ts_i; rt_printf("槽号\t值\r\n"); for (i = 0; i < EQU_SLOT_NUM_MAX; i++) // xj 2015-5-4 { rt_printf("%02d:\tvalue=0x%08lx_%08lx,inv=0x%08lx_%08lx\r\n", i, g_di[i].value[1], g_di[i].value[0], g_di[i].inv[1], g_di[i].inv[0]); } rt_printf("\r\n"); slot = g_di_slot; index = 1; ts_i.n = g_di[slot].ts_i[index].n; rt_printf("开入时间戳记录:slot=%d,index=%d,ts_i=%d.\r\n", slot, index, ts_i.n); rt_printf("序号\t值\t时间戳\r\n"); ts_i.n = 0; for (i = 0; i < DIDO_TS_NUM; i++) { rt_printf("%02d\t%d\t%08lu\r\n", ts_i.n, g_di[slot].ts_v[index][ts_i.n], g_di[slot].ts_t[index][ts_i.n]); ts_i.n++; } rt_printf("开出状态:0x%04x\r\n\r\n", g_do_status); rt_stat_printf(&g_stat_di_delay); rt_stat_printf(&g_stat_do_delay); return 0; } int dido_stat_reset(void) { rt_stat_init(&g_stat_di_delay, "di_delay"); rt_stat_init(&g_stat_do_delay, "do_delay"); return 0; } int dido_single_test(u8 slot, u8 point) { dido_do(slot, (1 << point), 1); ustimer_delay(250 * USTIMER_MS); dido_do(slot, (1 << point), 0); return 0; } int dido_test(void) { static unsigned long us0 = 0; static char dido_test_pair[3][2] = { {1, 3}, {2, 4}, {5, 5}, }; static char dido_test_di[32] = { 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 9, 10, 11, 8, 9, 10, 11, 12, 13, 10, 11, 12, 13, 12, 13}; int i, j, k, err_once, di_num; u32 slot_di, slot_do; if (g_test_on == 0) { return 0; } // 5秒处理一次 if (ustimer_delay_origin2(&us0, 10 * USTIMER_SEC) != 1) { return 0; } rt_printf("dido_test begin...\r\n"); ustimer_delay(1 * USTIMER_SEC); #if 1 // 测试开出板、开入板 // 开出板逐一开出 rt_printf("dido_test single.\r\n"); err_once = 0; for (i = 0; i < 3; i++) { slot_do = dido_test_pair[i][0]; slot_di = dido_test_pair[i][1]; for (j = 0; j < equ_get_do_num(slot_do); j++) { dido_do(slot_do, (1 << j), 1); ustimer_delay(250 * USTIMER_MS); di_num = equ_get_di_num(slot_di); if (slot_di == equ_get_slot_by_type(BOARD_TYPE_AUX)) { di_num -= 1; } for (k = 0; k < di_num; k++) { // 是否对应的开出点 if (dido_test_di[k] == j) { if ((g_di[slot_di].value[0] & (1 << k)) == 0) { err_once++; error_count++; rt_printf("dido_test on err(err=%d):i=%d,j=%d,k=%d,di=0x%08lx.\r\n", err_once, i, j, k, g_di[slot_di].value[0]); } } else { if ((g_di[slot_di].value[0] & (1 << k)) != 0) { err_once++; error_count++; rt_printf("dido_test off err(err=%d):i=%d,j=%d,k=%d,di=0x%08lx.\r\n", err_once, i, j, k, g_di[slot_di].value[0]); } } } dido_do(slot_do, (1 << j), 0); ustimer_delay(20 * USTIMER_MS); watchdog_feed_mainloop(); } } #endif rt_printf("\r\n开入变位时间超出次数:%lu\r\n", di_error_count); rt_printf("name\t\t\tmin\tmax\tavg\tsum\t\tcnt\n"); rt_stat_printf(&g_stat_di_delay); rt_printf("\r\n"); for (i = 0; i < EQU_SLOT_NUM_MAX; i++) { if (g_do_time[i].slot != 0) { rt_printf("do_time[%d]:diff_max=%lu,diff_out_count=%lu\r\n", g_do_time[i].slot, g_do_time[i].diff_max, g_do_time[i].diff_out_count); } } rt_printf("\r\n"); rt_printf("dido_test end:err_once=%d,error_count=%lu!\r\n\r\n\r\n", err_once, error_count); return 0; } bool check_board_di_valid(u8 slot, u8 index) { u32 i; // 初始化取反标志 index -= 1; for (i = 0; i < g_equ_config->di_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; i < num; i++) { slot_do = dido_test_pair[i][0]; slot_di = dido_test_pair[i][1]; // 槽位暂存 dido_buf[i + i * 13] = slot_do; dido_buf[i + i * 13 + 5] = slot_di; // 取出对应槽位的板卡中DI和DO的数量 di_num = equ_get_di_num(slot_di); do_num = equ_get_do_num(slot_do); // dido_do(slot_do,(1<<5),1); //磁保持继电器 // 启动记时 us_begin = ustimer_get_origin(); // 逐一输出开出点 for (j = 0; j < V3_OUT_NUM; j++) { dido_do(slot_do, (1 << j), 1); if (j == 5 && bkc6close) { dido_do(slot_do, (1 << 5), 0); // 开出6若焊接为常闭输出,反操作为闭合 } ustimer_delay(150 * USTIMER_MS); memset(di_ok, 0, sizeof(di_ok)); do_errflag = DIGROUP; for (k = 0; k < 4; k++) { bool bIgnore = false; di_result = (g_di[slot_di].value[0] ^ g_di[slot_di].inv[0]); k += i * 2; if (test_di[j][k] == 17) { bIgnore = check_board_di_valid(slot_di, 17); } if (test_di[j][k] == 18) { bIgnore = check_board_di_valid(slot_di, 18); } if (!test_di[j][k] || bIgnore) { do_errflag--; } else { if (!(di_result & (1 << (test_di[j][k] - 1)))) // 对应开入无值 { do_errflag--; di_ok[k] = test_di[j][k]; } } } if (!do_errflag) // 开出对应的所有开入都没有值,即代表该开出有问题 { if (j < 8) { dido_buf[i + i * 13 + 1] |= (1 << j); // 开出错误标志 } else if (j < 16) { dido_buf[i + i * 13 + 2] |= (1 << (j - 8)); // 开出错误标志 } rt_printf("dido_test do err:i=%d,j=%d,di=%d,di_l=0x%04lx,di_h=0x%04lx.\r\n", i, j, di_ok[0], g_di[slot_di].value[0], g_di[slot_di].value[1]); } else { for (k = 0; k < 4; k++) { if (!di_ok[k]) continue; if ((di_ok[k] - 1) < 8) { dido_buf[i + i * 13 + 6] |= (1 << (di_ok[k] - 1)); } else if ((di_ok[k] - 1) < 16) { dido_buf[i + i * 13 + 7] |= (1 << (di_ok[k] - 1 - 8)); } else if ((di_ok[k] - 1) < 24) { dido_buf[i + i * 13 + 8] |= (1 << (di_ok[k] - 1 - 16)); } else if ((di_ok[k] - 1) < 32) { dido_buf[i + i * 13 + 9] |= (1 << (di_ok[k] - 1 - 24)); } else if ((di_ok[k] - 1) < 40) { dido_buf[i + i * 13 + 10] |= (1 << (di_ok[k] - 1 - 32)); } else if ((di_ok[k] - 1) < 48) { dido_buf[i + i * 13 + 11] |= (1 << (di_ok[k] - 1 - 40)); } else if ((di_ok[k] - 1) < 56) { dido_buf[i + i * 13 + 12] |= (1 << (di_ok[k] - 1 - 48)); } else if ((di_ok[k] - 1) < 64) { dido_buf[i + i * 13 + 13] |= (1 << (di_ok[k] - 1 - 56)); } rt_printf("dido_test di err:i=%d,j=%d,di=%d,di_l=0x%04lx,di_h=0x%04lx.\r\n", i, j, di_ok[k], g_di[slot_di].value[0], g_di[slot_di].value[1]); } } dido_do(slot_do, (1 << j), 0); if (j == 5 && bkc6close) { dido_do(slot_do, (1 << 5), 1); // 开出6若焊接为常闭输出,反操作为闭合 } // if(j==4) //{ // dido_do(slot_do,(1<<5),1); //磁保持继电器 //} ustimer_delay(20 * USTIMER_MS); watchdog_feed_mainloop(); } } for (i = 0; i < EQU_SLOT_NUM_MAX; i++) { if (g_do_time[i].slot != 0) { rt_printf("do_time[%d]:diff_max=%lu,diff_out_count=%lu\r\n", g_do_time[i].slot, g_do_time[i].diff_max, g_do_time[i].diff_out_count); } } rt_printf("dido_test end:err_once=%d,error_count=%lu!\r\n\r\n\r\n", err_once, error_count); return 0; } int dido_auto_test(char *buf, int num) { #define KC_COUNT 11 // 12个开入,用5个开出进行测试 #if 0 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}, }; #endif static char test_di[KC_COUNT][DIGROUP] = { {1, 12, 23, 0}, {2, 13, 24, 0}, {3, 14, 0, 0}, {4, 15, 0, 0}, {5, 16, 0, 0}, {6, 17, 0, 0}, {7, 18, 0, 0}, {8, 19, 0, 0}, {9, 20, 0, 0}, {10, 21, 0, 0}, {11, 22, 0, 0}, // { 0, 0, 0,0} }; u8 brd_v3[] = {22, 23}; 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; rt_printf("g_brd_type_kz = %lu\r\n ", g_brd_type_kz); rt_printf("g_equ_config->do_num = %lu\r\n ", g_equ_config->do_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 < num; i++) { slot_do = dido_test_pair[i][0]; slot_di = dido_test_pair[i][1]; // 槽位暂存 dido_buf[i + i * 13] = slot_do; dido_buf[i + i * 13 + 5] = slot_di; // 取出对应槽位的板卡中DI和DO的数量 di_num = equ_get_di_num(slot_di); do_num = equ_get_do_num(slot_do); // dido_do(slot_do,(1<<5),1); //磁保持继电器 // 启动记时 us_begin = ustimer_get_origin(); // 逐一输出开出点 for (j = 0; j < KC_COUNT; j++) { dido_do(slot_do, (1 << j), 1); if (j == 5 && bkc6close) { dido_do(slot_do, (1 << 5), 0); // 开出6若焊接为常闭输出,反操作为闭合 } ustimer_delay(150 * USTIMER_MS); memset(di_ok, 0, sizeof(di_ok)); do_errflag = DIGROUP; for (k = 0; k < 4; k++) { bool bIgnore = false; di_result = (g_di[slot_di].value[0] ^ g_di[slot_di].inv[0]); k += i * 2; if (test_di[j][k] == 17) { bIgnore = check_board_di_valid(slot_di, 17); } if (test_di[j][k] == 18) { bIgnore = check_board_di_valid(slot_di, 18); } if (!test_di[j][k] || bIgnore) { do_errflag--; } else { if (!(di_result & (1 << (test_di[j][k] - 1)))) // 对应开入无值 { do_errflag--; di_ok[k] = test_di[j][k]; } } } if (!do_errflag) // 开出对应的所有开入都没有值,即代表该开出有问题 { if (j < 8) { dido_buf[i + i * 13 + 1] |= (1 << j); // 开出错误标志 } else if (j < 16) { dido_buf[i + i * 13 + 2] |= (1 << (j - 8)); // 开出错误标志 } rt_printf("dido_test do err:i=%d,j=%d,di=%d,di_l=0x%04lx,di_h=0x%04lx.\r\n", i, j, di_ok[0], g_di[slot_di].value[0], g_di[slot_di].value[1]); } else { for (k = 0; k < 4; k++) { if (!di_ok[k]) continue; if ((di_ok[k] - 1) < 8) { dido_buf[i + i * 13 + 6] |= (1 << (di_ok[k] - 1)); } else if ((di_ok[k] - 1) < 16) { dido_buf[i + i * 13 + 7] |= (1 << (di_ok[k] - 1 - 8)); } else if ((di_ok[k] - 1) < 24) { dido_buf[i + i * 13 + 8] |= (1 << (di_ok[k] - 1 - 16)); } else if ((di_ok[k] - 1) < 32) { dido_buf[i + i * 13 + 9] |= (1 << (di_ok[k] - 1 - 24)); } else if ((di_ok[k] - 1) < 40) { dido_buf[i + i * 13 + 10] |= (1 << (di_ok[k] - 1 - 32)); } else if ((di_ok[k] - 1) < 48) { dido_buf[i + i * 13 + 11] |= (1 << (di_ok[k] - 1 - 40)); } else if ((di_ok[k] - 1) < 56) { dido_buf[i + i * 13 + 12] |= (1 << (di_ok[k] - 1 - 48)); } else if ((di_ok[k] - 1) < 64) { dido_buf[i + i * 13 + 13] |= (1 << (di_ok[k] - 1 - 56)); } rt_printf("dido_test di err:i=%d,j=%d,di=%d,di_l=0x%04lx,di_h=0x%04lx.\r\n", i, j, di_ok[k], g_di[slot_di].value[0], g_di[slot_di].value[1]); } } dido_do(slot_do, (1 << j), 0); if (j == 5 && bkc6close) { dido_do(slot_do, (1 << 5), 1); // 开出6若焊接为常闭输出,反操作为闭合 } // if(j==4) //{ // dido_do(slot_do,(1<<5),1); //磁保持继电器 //} ustimer_delay(20 * USTIMER_MS); watchdog_feed_mainloop(); } } for (i = 0; i < EQU_SLOT_NUM_MAX; i++) { if (g_do_time[i].slot != 0) { rt_printf("do_time[%d]:diff_max=%lu,diff_out_count=%lu\r\n", g_do_time[i].slot, g_do_time[i].diff_max, g_do_time[i].diff_out_count); } } rt_printf("dido_test end:err_once=%d,error_count=%lu!\r\n\r\n\r\n", err_once, error_count); return 0; } /*------------------------------ 文件结束 ------------------------------------- */