阮工单片机编程经验集V2.1:如何做稳定单片机程序 , 阮丁远:
===================================================================
误把 3.3V步进输出端口Vcc即5V上,导致写w25q一直失败和混乱!
===================================================================
晶振可能太低,导致串口波特率误差过大,串口接收乱码经常发生,晶振可以提高到25mhz , 但也有可能是串口中断占更多cpu处理代码,导致串口中断,数据丢失!
但也可能在其他函数中 EA=0; _itoa(num,。。, EA=1;禁止间隙中断,导致串口中断功能丢失,导致串口字节丢失的概率bug
===================================================================
一个正式的环境,一个调试环境,实际上是公共的 类序列化 存储文件后,导致异常:
get_bytes_from_obj(p_装货确认_arr, log.get_cur_path() "/bakjindu/装货确认" index1.ToString() ".dat");
get_bytes_from_obj(监控距离队列, log.get_cur_path() "/bakjindu/监控距离队列" index1.ToString() ".dat");
,加is_sandbox.ToString() , 改为:
get_bytes_from_obj(p_装货确认_arr, log.get_cur_path() "/bakjindu/" is_sandbox.ToString() "装货确认" index1.ToString() ".dat");
get_bytes_from_obj(监控距离队列, log.get_cur_path() "/bakjindu/" is_sandbox.ToString() "监控距离队列" index1.ToString() ".dat");
===================================================================
data_geted_time_$rnd_varname_pre$后续赋值为0while(data_is_ok_$rnd_varname_pre$==0&&data_geted_time_$rnd_varname_pre$<=3200的直接 被sdcc优化器优化,直接变成while(1==1)卡死:,可以用volatile关键词:
volatile u8 data_is_ok_$rnd_varname_pre$=0;
volatile u32 data_geted_time_$rnd_varname_pre$=0;
...,
----------------------------------------------------
u8 get_updatw_$rnd_varname_pre$(){ data_is_ok_$rnd_varname_pre$=0; data_geted_time_$rnd_varname_pre$=0;
data_is_need_up_$rnd_varname_pre$=1; EA=1; //TI=0;RI=0; while(data_is_ok_$rnd_varname_pre$==0&&data_geted_time_$rnd_varname_pre$<=3200){ data_is_need_up_$rnd_varname_pre$=1 ; } return data_is_ok_$rnd_varname_pre$; }
===================================================================
如果单片机中断断中更新数据,如果在其他非中断中实时读取数据,则很容易更新数据到一半读取数据,导致编码混乱, 可以用缓存 原子操作即EA=0; 赋值。。。 EA=1;来解决
===================================================================
用java 启动命令运行java包时容易cmd卡住窗户,按回车继续运行。
改为用javaw:
javaw -jar meterservice0.jar --spring.profiles.active=prod
===================================================================
SDCC例如,使用定义变量 __code xxxxx或 __xdata ,
那么用extern还应添加引用
__code 或 __xdata :
比如extern __code xxxxxx
===================================================================
lcd当1602并口屏幕显示整数字时,如何解决偶尔的花屏
SDCC的 _ltoa等函数要先EA=0;可调用,否则进中断 后 ,结果值是乱码!,检查一切 加上类似的库函数EA=0;调用完再EA=1:
EA=0; _ltoa(num, temp1str, 10);//比_itoa范围大
EA=1;
===================================================================
加 :if(us100_count_$rnd_varname_pre$>260){ //防止累积,最终死机,因为进不去if(us100_count_$rnd_varname_pre$>170&&us100_count_$rnd_varname_pre$<250)了:
[$interrupt_1] @@int_num=#get_interrupt_num_set_code(group_xml,rand1,block_name,ref_complier,extinfo,"[($_xgm$value3),type=pin]")%
static u8 Ray_Flag_$rnd_varname_pre$=0; us100_enable_$rnd_varname_pre$=1; if(us100_count_$rnd_varname_pre$>110&&us100_count_$rnd_varname_pre$<160//9ms的低电平和4.5ms高电平为引导码,大于11ms即可,也即>110 { Ray_Flag_$rnd_varname_pre$=1; //标志位写1 Ray_Read_bit_count_$rnd_varname_pre$=0; //接收到引导信号,编号归0、 //Ray_Read_Buffer_$rnd_varname_pre$[4]++; //遥控器按着不放,会连续发送N个引导信号 }
if(us100_count_$rnd_varname_pre$>170&&us100_count_$rnd_varname_pre$<250)//0.565ms低电平和20MS高电平 { Ray_Flag_$rnd_varname_pre$=2; //标志位写1 Ray_Read_bit_count2_$rnd_varname_pre$=0; //接收到引导信号,编号归0、 //Ray_Read_Buffer_$rnd_varname_pre$[4]++; //遥控器按着不放,会连续发送N个引导信号 }
if(us100_count_$rnd_varname_pre$>260){ //防止一直累加,而最终死机,因为进入不了if(us100_count_$rnd_varname_pre$>170&&us100_count_$rnd_varname_pre$<250)了
us100_enable_$rnd_varname_pre$=0;
us100_count_$rnd_varname_pre$=0;
}
if(Ray_Flag_$rnd_varname_pre$==1) //如果之前的信号是引导 { Ray_bit_timer_Buffer_$rnd_varname_pre$[Ray_Read_bit_count_$rnd_varname_pre$]=us100_count_$rnd_varname_pre$;//把定时器走时变量,赋值给数组缓存 us100_count_$rnd_varname_pre$=0; //时间重新累计 Ray_Read_bit_count_$rnd_varname_pre$++; //编号自动增加1 if(Ray_Read_bit_count_$rnd_varname_pre$>=36) //起始码+35位数据 { Ray_Read_bit_count_$rnd_varname_pre$=0; //编号清0 us100_count_$rnd_varname_pre$=0; //时间重新累计 //Ray_Read_ok_$rnd_varname_pre$=1; //允许CPU执行解码功能 Ray_Flag_$rnd_varname_pre$=0; //标志为写0表示一帧红外接收已经完成了 } }
if(Ray_Flag_$rnd_varname_pre$==2) //如果之前的信号是引导 { Ray_bit_timer_Buffer2_$rnd_varname_pre$[Ray_Read_bit_count2_$rnd_varname_pre$]=us100_count_$rnd_varname_pre$;//把定时器走时变量,赋值给数组缓存 us100_count_$rnd_varname_pre$=0; //时间重新累计 Ray_Read_bit_count2_$rnd_varname_pre$++; //编号自动增加1 if(Ray_Read_bit_count2_$rnd_varname_pre$>=33) //起始码+32位数 { Ray_Read_bit_count2_$rnd_varname_pre$=0; //编号清0 us100_count_$rnd_varname_pre$=0; //时间重新累计 Ray_Read_ok_$rnd_varname_pre$=1; //允许CPU执行解码功能 Ray_Flag_$rnd_varname_pre$=0; //标志为写0表示一帧红外接收已经完成了 us100_enable_$rnd_varname_pre$=0; //暂时关闭定时器0
} }
===================================================================
stc12c5a60s2:
P0M0 = 0x00;
P0M1 = 0x00; //P0口如果插着lcd1602,会导致P0不推挽输出的话就驱动不了led灯的怪问题
===================================================================
EA=0;//防止Ray_Read_user_data_$rnd_varname_pre$=Ray_Read_Buffer_$rnd_varname_pre$[2]的赋值被中途中断打乱而乱码 :
void Ray_Decode_Drive_$rnd_varname_pre$() { u8 i,j,k=1,value=0,tempv; if(Ray_Read_ok_$rnd_varname_pre$==1)//如果已经完成一帧红外 33个时间长度的保存 {
EA=0;//防止Ray_Read_user_data_$rnd_varname_pre$=Ray_Read_Buffer_$rnd_varname_pre$[2]的赋值被中途中断打乱而乱码
Ray_Read_ok_$rnd_varname_pre$=0;//取反,防止第二次进来 TR1=0; //暂时关闭定时器0 for(i=0;i<4;i++)//一帧红外是四个字节 { for(j=0;j<8;j++)//每个字节是8个时间长短 { value>>=1;//先左移后判断 if(Ray_bit_timer_Buffer_$rnd_varname_pre$[k]>17)//此参数为定时器定时102us所选值 { value=value|0x80;//如果这个位的时间大于17*102us,既1700us以上是数据1 } else { value=value&0x7f;//否则 这个位的时间小于17*102us,既1700us以下是数据0 } k++;//判断下一个bit时间 } if(i==3)//如果是第三字节 { if(value!=Ray_Read_Buffer_$rnd_varname_pre$[3]) //如果新接收的第3字节和上次保存的第3字节不一样 { Ray_Read_Buffer_$rnd_varname_pre$[4]=255;//说明换了按钮了 Ray_Read_Buffer_$rnd_varname_pre$[5]=255; } if(value==Ray_Read_Buffer_$rnd_varname_pre$[3]) //如果新接收的第3字节和上次保存的第3字节完全一样 { Ray_Read_Buffer_$rnd_varname_pre$[5]++; //连按加加 Ray_Read_Buffer_$rnd_varname_pre$[4]=255;//长按归0 (255+1之后是0) } } Ray_Read_Buffer_$rnd_varname_pre$[i]=value;//解码的字节拷贝给数组 }
tempv=Ray_Read_Buffer_$rnd_varname_pre$[2];//防止Ray_Read_user_data_$rnd_varname_pre$=Ray_Read_Buffer_$rnd_varname_pre$[2]的赋值被中途中断打乱而乱码
if(tempv==(u8)(~Ray_Read_Buffer_$rnd_varname_pre$[3]) && Ray_Read_Buffer_$rnd_varname_pre$[0]==(u8)(~Ray_Read_Buffer_$rnd_varname_pre$[1]) ){
Ray_Read_user_data_$rnd_varname_pre$=tempv;
}
EA=1;
} }
===================================================================
如果调用函数在别的c文件里时, 单片机 c语言里不在调用函数所在c文件里头部定义函数申明的话 可能会出奇怪现象
--
struct A a;
fun(a) 这样代入可能有怪问题
需要: fun(&a) 这样代入,
而fun定义为 fun(struct A * a1){
.......
}
=================================================================== c#里
A a=new a();
....
a=new a();
.....
和
A a1=new a();
A a2=new a();
A a=a1; .....
a=a2;
......
A b=a1;
的区别 ???
===================================================================
小数转整数时未先四舍五入而导致的0.99999丢失的问题:
val=6.7,
乘以10后:
process_exped_val=66.9999980926514
(int)process_exped_val=66
public static int V100_convert_to_int_with_math_round(double val) {
return (int)Math.Round(val, MidpointRounding.AwayFromZero); //
}
-------------------
或者 为 (int)Math.Round(val)而未加 MidpointRounding.AwayFromZero参数,而导致四舍五入异常!,比如漏电流始终偶尔校不准!
===================================================================
加 ,id ASC:
SELECT * FROM kb0_ui WHERE is_use_zengliang_tongji=1 AND kb0_idstr='t_yi1xek3dhp' AND dianwei_name='dianneng_zong' AND (ADDTIME BETWEEN '2021/01/01 00:00:00' AND '2022/01/01 00:00:00') ORDER BY diff_value1 DESC ,id ASC LIMIT 0,22
比
SELECT * FROM kb0_ui WHERE is_use_zengliang_tongji=1 AND kb0_idstr='t_yi1xek3dhp' AND dianwei_name='dianneng_zong' AND (ADDTIME BETWEEN '2021/01/01 00:00:00' AND '2022/01/01 00:00:00') ORDER BY diff_value1 DESC LIMIT 0,22
要快!!,
或者ORDER BY round(diff_value1,5) DESC 也快
--------------------------------
company_id LIKE '...' 反而比company_id ='...'要快,可能改为 company_id LIKE '...' 后主键索引变为了ADDTIME ,
explain 分析 key_len从171变小为 17了 :
SELECT SUM(diff_value1) AS zong_diff1,date_hour_index FROM kb0_ui WHERE is_use_zengliang_tongji=1 AND dianwei_name='dianneng_zong' AND (company_id LIKE 't_uolcoya67e') AND (ADDTIME >= '2021-11-04 00:00:00' ) AND ADDTIME <= '2021-11-04 23:59:59' GROUP BY date_hour_index
比
SELECT SUM(diff_value1) AS zong_diff1,date_hour_index FROM kb0_ui WHERE is_use_zengliang_tongji=1 AND dianwei_name='dianneng_zong' AND (company_id = 't_uolcoya67e') AND (ADDTIME >= '2021-11-04 00:00:00' ) AND ADDTIME <= '2021-11-04 23:59:59' GROUP BY date_hour_index
===================================================================
1.程序里涉及清空缓存文件时且可以选择自定义导出目录时,务必不要删除文件操作, 万一用户选择的是桌面或其他系统目录,则全当为垃圾缓存而被删除且不可恢复!!!
,在线升级前如果要删除老文件,那要保证安装目录 是含子目录 的特定名比如 c:\soft001 ,以防止误删其他文件和目录,比如万一安装在硬盘根目录
删除前最好做备份
2. 检查所有线程里有无return,防止意外return
=================================================================== 服务端所有时间日期格式化为 字符串时,统一格式设为 yyyy-MM-dd HH:mm:ss ,防止操作系统设置日期与区域的格式时影响这个格式,而发生意外格式 ===================================================================
MySql等里面的float字段只有7位有效数字,大数值时容易省略小数位而发生不准!,可以换为
double 字段或其他专用于金钱统计的字段
===================================================================
LMxxxx5.0的12-36V的转5V的dc-dc稳压芯片,输入脚必须并40V470uf的电解电容,特别是输入脚前串了防反接二极管时,不然容易烧管,可能是反向高电压无法通过防反接二极管
=================================================================== 如果串口 校验位为2,而设为1,会使奇偶校验位发生奇怪的异常,比如一个串口调试软件调试正常,而上位机收到很多 3f 3f 3f.....
===================================================================
多个线程里不要用 MessageBox之类,而要用变量标志,在其他单线程里 MessageBox,否则一出错就弹个不完消息框!!
===================================================================
所有检测步骤做成状态机式的,可以回退的,这样方便:比如电压输出没到位时,方便回退重试的,而不是死循环等待,也不方便停止按钮
=================================================================== 有些不稳定性是因为器件寿命,比如程序里错误的存在了每秒写flash 1000次的操作,则flash很快损坏!!! ,
对于频繁读入flash的操作,可以做个ram缓存区,修改flash和开机时才更新缓存。。。 ----------------------------
,另外上电时先要等待200ms至少,来上电稳定后再读取比如w25Q64的内容,不然没上电稳定就读,容易读出错码!!!
===================================================================
注意多个设备的通信协议调用的 crc16函数,有的是高字节在前,有的是低字节在前,如果公用一个 crc16函数,就会导致改掉一个,另一个就通信不通的现象
=================================================================== 关闭所有采集卡输入通道,防止切换电流源时跳闸而引发测跳闸时间时异常!:
qiehuan_caiji_boxingABC(99);//V888,关闭所有采集卡输入通道,防止切换电流源时跳闸而引发测跳闸时间时异常! ,另外再加异常跳闸时的重试合闸和重上电复位!!!
===================================================================
喂狗也是有讲究的,不要在可能重复产生中断的中断服务函数里面喂狗。 万一程序死翘翘了,但是中断可不会死,这时候在中断里面喂狗的话,程序就会在跑飞和中断服务函数中切换 ===================================================================
AD9里新建pcb时有时距离是三层板,导致布线时有些引脚怎么也不布的问题,删掉pcb中间层即可
=================================================================== 有的发生源有 2种读电压电流的接口,一种读设定值,另一种读实时值,判断电压到位没要读实时值那个接口!!!
===================================================================
对 关闭电压输出 等高风险的按钮事件,为保证100%实时响应,则用类似以下的机制,即传入第二个参数8,不然默认0,则按钮事件互斥:
p_void p1 = () => { close_all_UI(); libcls.MessageBoxw("已降源,请等待8秒!");//提示会有安全感 };
setrun(p1,8);
---
for (int i = 0; i < 9999; i++) { ingsetrun[i] = 0; }
--
public delegate void p_void();
int[] ingsetrun = new int[9999];
public void setrun(p_void p1,int lock_index=0) {
if (ingsetrun[lock_index] == 1) {
return;
}
ingsetrun[lock_index] = 1; try {
System.Threading.Thread pt1 = new System.Threading.Thread(new System.Threading.ThreadStart(p1));
pt1.Start();
//while (pt1.ThreadState == System.Threading.ThreadState.Running || pt1.ThreadState== System.Threading.ThreadState.Unstarted)
while (pt1.IsAlive) { Application.DoEvents(); System.Threading.Thread.Sleep(5);//10的话太卡,小点,因为Sleep太大的话退出等待要卡很久!!
}
} catch {
ingsetrun[lock_index] = 0; } ingsetrun[lock_index] = 0;
}
=================================================================== delegate {里的return 的作用域问题:
int reted1 = 0; this.Invoke(new EventHandler(delegate { Form2_Vatcmd_shoudong_set_botelv_webbrower pwwin1 = new Form2_Vatcmd_shoudong_set_botelv_webbrower(); pwwin1.sss2 = "请手动设置波特率到115200,然后点保存参数并重启串口服务器!"; pwwin1.refwin1 = this; if (pwwin1.ShowDialog() != System.Windows.Forms.DialogResult.OK) { reted1 = 1; return;
} })); if (reted1 == 1) {
return;
}
=================================================================== 用委托和事件解决2个类相互引用的问题:
//定义一个delegate委托 public delegate void read_change_event_ruan_func_ptr(int index1); //定义事件,类型为上面定义的read_change_event_ruan_func_ptr委托 public event read_change_event_ruan_func_ptr Onread1;
public void read_change_event_ruan(int index1) {
Onread1(index1);
}
,另一个类里:
m_processor.Onread1 += process_P_volt_to_idata_bytes;
public void process_P_volt_to_idata_bytes(int index1) { if ((byte)index1 == (byte)SFR.P0) { process_P_volt_to_idata_bytesDo((byte)SFR.P0, "P0");
} if ((byte)index1 == (byte)SFR.P1) { process_P_volt_to_idata_bytesDo((byte)SFR.P1, "P1");
} if ((byte)index1 == (byte)SFR.P2) { process_P_volt_to_idata_bytesDo((byte)SFR.P2, "P2");
} if ((byte)index1 == (byte)SFR.P3) { process_P_volt_to_idata_bytesDo((byte)SFR.P3, "P3");
}
}
===================================================================
如何调整WinForm界面ComboBox控件的高度
进击的路飞桑 2020-07-06 14:27:23 636 收藏 分类专栏: # C# 版权 打开ComboBox控件的属性页,调整字体即可改变其高度
=================================================================== 设备急停信号不要做成连续读plc是否急停标志位,而要做成2处:急停按钮控制plc急停,还有上位机软件里写1个w区的位来急停
=================================================================== 电流功率源的ict有些是发复位会强制断下电流一下,有些是如果没旁路则不会断一下,如果瞬间断一下源输出值可能异常!!
===================================================================
得做通讯使能:再上电稳定8秒后才开始上线通讯,不然没上电稳定就不断发通讯包,容易不稳定 ----
plc 别接地线,否则容易烧坏plc??
-- 232转485模块的铁外壳不要直接安装在设备外壳上固定,需要加绝缘板,否则容易导致TXD灯常亮等问题 ,其实不是模块问题,而是485转换模块的 232输入口的1号脚和5号脚这个gnd脚接反了,但是由于只用于发送而不接收,接反也能用, 但是不稳定,偶尔txd灯常亮。。。。。。。 ,所以对于只发送的通讯线路,出厂前要检查下232线序!!!
--
通信日志功能的重要性: 加了10工位的每个工位的通信日志功能后,可以看到有时清除断路器电量发包时返回心跳包的结果内容,说明心跳包和清除断路器电量发包冲突了,从而不稳定!!!!
===================================================================
NO.0000: 比如重发包超时时间为80ms,而断路器返回包的等待时间有100ms,则会在收到返回包前重发一个包,导致最终收到2个返回包, 而第2个返回包就干扰了后续发包的返回包,造成通讯纹乱: 所以: 所有包的重发包超时时间改大到1秒,原来只有120ms左右
,即加 if (isnowait == 0)//v1002: {
//防止重发包的返回包干扰下面的续包: if (sleep_with_rt_ok(1 * 1000, gongwei_index, comm_index) == 1) { sended = 1; break;
}
}
:
public int SendPortSP_try_more(string v20_sub_cmd_is_dl_or_68xieyi, int gongwei_index, int is_rec_senddata_str0_or_rec_senddata_len1, int comm_index, string cmd1, byte[] ombuffer, int wait_sec = -1, int isnowait = -1, int isPLC = -1, int is_stopmsg_err1 = 0, int is_nowait_for_liji_closeV = 0, int recv_mubiao_length = 0, byte headbyte = 0, string sub_cmd = "", float wait_sec_after_write=-1) { .........................
if (wait_sec_after_write >= 0.01f) {
if (sleep_with_rt_ok(wait_sec_after_write * 1000, gongwei_index,comm_index) == 1) { sended = 1; break;
}
}
if (isnowait == 0)//v1002: {
//防止重发包的返回包干扰下面的续包: if (sleep_with_rt_ok(1 * 1000, gongwei_index, comm_index) == 1) { sended = 1; break;
}
} =================================================================== NO1。 所有textBox和checkbox, radioButton等全部要用拼音尾缀来命名,不能用数字尾缀来命令,否则容易 弄错textBox,而导致隐藏的bug几个月
-----------------
有很多按钮的界面,可以把按钮的事件做成一个,然后switch (button.Name)一下,比分散写到各个事件函数里要方便:
private void f1_h2004_Click(object sender, EventArgs e) { Button button = (Button) sender; switch (button.Name) { case "f1_h2004": this.x_Omron.写[0].value_D500[0x1b] = 1; this.x_Omron.写[0].cmd_D500[0x1b] = true; break;
case "f1_h2005":
===================================================================
vs2010里如果复制某个界面的控件到新工程,那么所有输入框事件和combox控件的改变事件都要手工拷贝来,不然很可能改值后保存不住,
而引发写入参数不是改动的值,从而参数始终不变而改不了,误以为写成功了,比如跳闸阀值,这样有安全风险!!!!
=================================================================== 选择性序列化:
[Serializable] public class MyObject { public int n1; [NonSerialized] public int n2; public String str; }
===================================================================
验证断路器等写入的参数是否正确写入时,缓存原始写入值的变量最好带绝对地址,否则按变量索引增量时容易出日后的兼容性问题:
//v3.3: public int get_val_by_writed_d_addr(int addr11, List<v1000_val_cls> writed_d) {
foreach (v1000_val_cls in1 in writed_d) { if (in1.addr == addr11) {
return in1.val1;
}
} return -1;
}
===================================================================
int binval = (int)(Math.Round(Val_to_jiaozhuan) * chengyi_100_or_1000);
如果Val_to_jiaozhuan为零点几,就会异常!!,要注意,所以改为int binval = (int)(Math.Round(Val_to_jiaozhuan* chengyi_100_or_1000) );
===================================================================
启动主校准进程之类后,再次开始时,除了判断 是否校准中 变量外,还要判断这个主校准进程是否isalive和running
===================================================================
像jieti的断路器板子,校准时 增益点和偏移点的给定电压电流值必须为20%的比率关系,不然校不准!
===================================================================
像今闰的三相源读电压电流等返回1失败后,需要再重试2-4次!!!,不然不稳定
=================================================================== ict报警旁路打开,可能不是产品和夹具的问题,可能是铜排没有拧紧而接触不良,也可能上铜排和下铜排同时接触不良,都要拧紧才能解决
---
断路器计量型的产品不同系列可能脉冲常数不一样,导致脉冲误差检测老是50%误差之类
===================================================================
同一时刻同时2个电压源报警保护,很可能是源间回路有短路,比如换相开关和电流源的换相时的回路导致ab相间短路
,另外,如果电压源老过载保护或IGBT保护而断电,则可以串一个50欧到100欧左右的电阻,可以防止保护
=================================================================== 机械手取料时料框里放个复杂图案,机械手上装摄像头来模板识别这个图案,获得料框的xy偏移,也可以用于料分类的识别, 料框里用放料孔夹死料,来限位料,这样不用视觉也可以准确取料,
===================================================================
小220v继电器换为大的220V交流接触器,是否灵敏度降低?,从而自动在毛刺时不跳
=================================================================== 写串口等写函数不要存在2个以上的线程在同时写串口,即使是加了lock(obj) ,可以做成1个写thread, 然后这个 thread里接收其他线程的发串口命令,来一个一个发包,
这样可以防止比如心跳包的返回干扰其他包的返回,然后写thread里2次写间隔个20ms最好, ------- 最好做成执行完几步后进行一步校验等,比如执行完。。。。步后最后对比电压电流值是否校准通过,然后不通过时可以重试,这样即使哪步丢包或错误,也可以最终自动修正, 这样程序的稳定性和鲁棒性好点!!!
===================================================================
移植老项目到新项目里时,务必先烤出来一份,再复制,防止改乱老的
===================================================================
有时候断路器校准完不准,不是校准本身问题,还可能是电压电流可能是BCD码或解析异常,导致实际通讯过来的值不准!!
不要实时通讯监测产品上线否,而要再产品上电完成后等待5秒左右后再开始通讯,防止刚通电不稳定而乱写初始化sn等
===================================================================
UT1616串口服务器的串口接收超时设置 时间不能大于50ms?? :需要比 send...发包函数里的 wait返回信号量 的延时值要小!!
,不然容易一次收到2个包而导致校验不通过?
===================================================================
双倍误差原理,漏电一直校验不准:
不代入当前漏电流值: _Current_LD;,否则因为_Current_LD不是实时的,从而导致漏电纠正的双倍误差!,所以还不如用漏电流设定值DC_cp_power_Lou_A:
if (is_jiaozhun_UI1_or_Lou0_or_xiangwei2 == 0) {
U_innn = 0; I_innn = 0;
U_innn2 = 0; I_innn2 = 0;
U_innn3 = 0; I_innn3 = 0;
Lou_innn = DC_cp_power_Lou_A * 1000;//不代入 _Current_LD;,否则因为_Current_LD不是实时的,从而导致漏电纠正的双倍误差!
-----------
不过以上也可能是 (byte)( (int)(Lou_innn)&0xff),(byte)(((int)(Lou_innn)>>8)&0xff) 里的漏电流float值转为int值,比如29.999 ma,会自动变为29ma,
而不是自动四舍五入而变为 30ma,从而漏电流精度有问题,而校不准
===================================================================
c#用委托类似函数指针,这样可以使2个类相互解耦,从而模块化,提高代码可阅读性
===================================================================
所有参数做成拼音变量名等,不要写死程数字,该抽象为函数的就新建立函数,这样:大改时可以批量搜索某个变量名来定位所有相关点而不会漏掉,从而防止bug
=================================================================== this.FormBorderStyle = FormBorderStyle.Fixed3D;//需要关闭按钮, 不然卡死时关不了!!!
===================================================================
internal static class Program { [STAThread] private static void Main() { bool flag; Mutex mutex = new Mutex(true, "HXV001", out flag); if (flag) { mutex.ReleaseMutex(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } else { MessageBox.Show("只能运行一个程序!"); } } }
===================================================================
随机性的不稳定也可能是 参数最大值为65535,而设为900000等之类,导致参数实际为30左右,这样的阀值遇到随机性的电流时时而过,时而没过,从而随机异常!
===================================================================
去掉最左边的空列和禁止自动添加新行,禁止拖拉列宽行宽,禁止选中行等:
public void add_col(DataGridView inn,string id,string name,int width) {
DataGridViewColumn column = new DataGridViewTextBoxColumn(); column.HeaderText = name; column.Name = id; column.Width = width; column.SortMode = DataGridViewColumnSortMode.NotSortable; //column.CellTemplate = dgvcell;//设置模板 inn.Columns.Add(column);
}
public void add_row(DataGridView inn,string first_name,int[] init_cols) {
int index1= inn.Rows.Add();
inn.Rows[index1].Cells[0].Value = first_name;
foreach (int closindex in init_cols) {
inn.Rows[index1].Cells[closindex+1].Style.BackColor = Color.Green;
}
} public void init_datadgirdview_do1(DataGridView inn) {
inn.EnableHeadersVisualStyles = false;
inn.RowHeadersVisible = false;//去掉最左边的空列
inn.AllowUserToAddRows = false;
inn.AllowUserToResizeColumns = false;//设置datagridview的列宽不可被用户手动拖拉 inn.AllowUserToResizeRows = false;
inn.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
//dataGridView1_tiaoza.EditMode==DataGridViewEditMode.EditOnEnter
inn.CellBorderStyle = DataGridViewCellBorderStyle.Raised;//单元格无边框化
inn.DefaultCellStyle.BackColor = this.dataGridView1_tiaoza.Parent.BackColor;
inn.BorderStyle = BorderStyle.None;
inn.BackgroundColor = this.dataGridView1_tiaoza.Parent.BackColor;
}
public void init_datadgirdview_yaoxin() {
init_datadgirdview_do1(dataGridView1_tiaoza);
add_col(dataGridView1_tiaoza, "id", "跳闸", 100); add_col(dataGridView1_tiaoza, "zong", "总线路", 68);
add_col(dataGridView1_tiaoza, "a", "A相", 66);
add_col(dataGridView1_tiaoza, "b", "B相", 66);
add_col(dataGridView1_tiaoza, "c", "C相", 66);
add_col(dataGridView1_tiaoza, "N", "零线", 66);
add_row(dataGridView1_tiaoza, "微断死锁状态",new int[]{0}); add_row(dataGridView1_tiaoza, "过流保护", new int[] { 1,2,3 }); add_row(dataGridView1_tiaoza, "过载保护异常", new int[] { 1, 2, 3 }); add_row(dataGridView1_tiaoza, "过压保护", new int[] { 1, 2, 3 }); add_row(dataGridView1_tiaoza, "欠压保护", new int[] { 1, 2, 3 }); add_row(dataGridView1_tiaoza, "线路电弧", new int[] { 1, 2, 3 }); add_row(dataGridView1_tiaoza, "高温保护", new int[] { 1, 2, 3,4 }); add_row(dataGridView1_tiaoza, "漏电保护", new int[] { 0 }); add_row(dataGridView1_tiaoza, "三相不平衡", new int[] { 0 }); add_row(dataGridView1_tiaoza, "缺相保护", new int[] { 0 });
init_datadgirdview_do1(dataGridView_gaojing);
add_col(dataGridView_gaojing, "id", "告警", 100); add_col(dataGridView_gaojing, "zong", "总线路", 68);
add_col(dataGridView_gaojing, "a", "A相", 66);
add_col(dataGridView_gaojing, "b", "B相", 66);
add_col(dataGridView_gaojing, "c", "C相", 66);
add_col(dataGridView_gaojing, "N", "零线", 66);
add_row(dataGridView_gaojing, "过流保护", new int[] { 1, 2, 3 }); add_row(dataGridView_gaojing, "过载保护异常", new int[] { 1, 2, 3 }); add_row(dataGridView_gaojing, "过压保护", new int[] { 1, 2, 3 }); add_row(dataGridView_gaojing, "欠压保护", new int[] { 1, 2, 3 }); add_row(dataGridView_gaojing, "线路电弧", new int[] { 1, 2, 3 }); add_row(dataGridView_gaojing, "高温保护", new int[] { 1, 2, 3,4 }); add_row(dataGridView_gaojing, "漏电保护", new int[] { 0 }); add_row(dataGridView_gaojing, "三相不平衡", new int[] { 0 }); add_row(dataGridView_gaojing, "缺相保护", new int[] { 0 });
}
private void dataGridView1_tiaoza_SelectionChanged_1(object sender, EventArgs e) { dataGridView1_tiaoza.ClearSelection(); }
private void dataGridView_gaojing_SelectionChanged(object sender, EventArgs e) { dataGridView_gaojing.ClearSelection(); }
private void dataGridView_qita_SelectionChanged(object sender, EventArgs e) { dataGridView_qita.ClearSelection(); }
===================================================================
上层是个循环,所以这里不能用 return;,需要 continue:
if (client1_To == null) {
showmsg(ip1 + " toid not find!");
myClientSocket.Send(Encoding.ASCII.GetBytes("toid not find!")); continue;//上层是个循环,所以这里不能用 return;,需要 continue }
=================================================================== 有重发机制的返回包时最好不只用if ( rttobj.recv_ok1b == 1) 之类,而用当前包的cur_cmd字符串经由hashtable来分开置 recv_ok1b,这样 可以防止上一个包的确认包影响当前包的recv_ok1b而漏包 , 或者用 if(recv_buf[2]==100&&cur_cmd="xintiaobao" )机制来判断,但这容易永不成立???
===================================================================
以下里的if ( rttobj.recv_ok1b != 1) 可以删掉,防止上一个包的确认包影响当前包的recv_ok1b而漏包!!!:
while (isrunning == 1 && max_retrycount <= maxccc) {
//if ( rttobj.recv_ok1b != 1) {
rttobj.v16_buffer.Clear();//v90 rttobj.v40_cur_datastep = 0;//v90
if (rttobj.v20_is_UT6616 == 0) {
rttobj.v16_data_recv_timeout_enable = 1;//old rttobj.v16_data_recv_timeout_ccc = DateTime.Now;//old
rttobj.OP_s.SP.Write(ombuffer, 0, ombuffer.Length); } else {
Connect(rttobj.gongwei_index); SendData(ombuffer, rttobj.gongwei_index);
}
if (wait_sec_after_write >= 1) {
if (sleep_with_rt_ok(wait_sec_after_write * 1000, gongwei_index) == 1) { sended = 1; break;
}
}
}
--------------------------
===================================================================
有时候返回包了但功能没执行到位,可能不是因为其本身,而且启动代码有问题,导致已经在工作的假象
=================================================================== 读参数到输入框前先清空输入框,防止本次读失败,而遗传上次的输入框值!!
===================================================================
发包函数里的发包状态清零语句前最好加 System.Threading.Thread.Sleep(3);,防止下一个包影响上一个包的发包代码后的if(recv_ok1b!=1.........:
public int SendPortSP_try_more(string v20_sub_cmd_is_dl_or_68xieyi, int gongwei_index, int is_rec_senddata_str0_or_rec_senddata_len1, int comm_index, string cmd1, byte[] ombuffer, int wait_sec = -1, int isnowait = -1, int isPLC = -1, int is_stopmsg_err1 = 0, int is_nowait_for_liji_closeV = 0, int recv_mubiao_length