|
阮工的單片機編程經(jīng)驗集:如何做穩(wěn)定單片機程序與上位機程序防卡頓,js等經(jīng)驗;阮丁遠(yuǎn)于20211109 :
===================================================================
小數(shù)轉(zhuǎn)整數(shù)時未先四舍五入而導(dǎo)致的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參數(shù),而導(dǎo)致四舍五入異常!,比如漏電流始終偶爾校不準(zhǔn)!
===================================================================
加 ,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 '...' 后主鍵索引變?yōu)榱薃DDTIME ,
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.程序里涉及清空緩存文件時且可以選擇自定義導(dǎo)出目錄時,務(wù)必不要刪除文件操作,
萬一用戶選擇的是桌面或其他系統(tǒng)目錄,則全當(dāng)為垃圾緩存而被刪除且不可恢復(fù)。!
,在線升級前如果要刪除老文件,那要保證安裝目錄 是含子目錄 的特定名比如 c:\soft001 ,以防止誤刪其他文件和目錄,比如萬一安裝在硬盤根目錄
刪除前最好做備份
2.
檢查所有線程里有無return,防止意外return
===================================================================
服務(wù)端所有時間日期格式化為 字符串時,統(tǒng)一格式設(shè)為 yyyy-MM-dd HH:mm:ss
,防止操作系統(tǒng)設(shè)置日期與區(qū)域的格式時影響這個格式,而發(fā)生意外格式
===================================================================
MySql等里面的float字段只有7位有效數(shù)字,大數(shù)值時容易省略小數(shù)位而發(fā)生不準(zhǔn)!,可以換為
double 字段或其他專用于金錢統(tǒng)計的字段
===================================================================
LMxxxx5.0的12-36V的轉(zhuǎn)5V的dc-dc穩(wěn)壓芯片,輸入腳必須并40V470uf的電解電容,特別是輸入腳前串了防反接二極管時,不然容易燒管,可能是反向高電壓無法通過防反接二極管
===================================================================
如果串口 校驗位為2,而設(shè)為1,會使奇偶校驗位發(fā)生奇怪的異常,比如一個串口調(diào)試軟件調(diào)試正常,而上位機收到很多 3f 3f 3f.....
===================================================================
多個線程里不要用 MessageBox之類,而要用變量標(biāo)志,在其他單線程里 MessageBox,否則一出錯就彈個不完消息框!!
===================================================================
所有檢測步驟做成狀態(tài)機式的,可以回退的,這樣方便:比如電壓輸出沒到位時,方便回退重試的,而不是死循環(huán)等待,也不方便停止按鈕
===================================================================
有些不穩(wěn)定性是因為器件壽命,比如程序里錯誤的存在了每秒寫flash 1000次的操作,則flash很快損壞!!
,
對于頻繁讀入flash的操作,可以做個ram緩存區(qū),修改flash和開機時才更新緩存。。。
----------------------------
,另外上電時先要等待200ms至少,來上電穩(wěn)定后再讀取比如w25Q64的內(nèi)容,不然沒上電穩(wěn)定就讀,容易讀出錯碼!!!
===================================================================
注意多個設(shè)備的通信協(xié)議調(diào)用的 crc16函數(shù),有的是高字節(jié)在前,有的是低字節(jié)在前,如果公用一個 crc16函數(shù),就會導(dǎo)致改掉一個,另一個就通信不通的現(xiàn)象
===================================================================
關(guān)閉所有采集卡輸入通道,防止切換電流源時跳閘而引發(fā)測跳閘時間時異常!:
qiehuan_caiji_boxingABC(99);//V888,關(guān)閉所有采集卡輸入通道,防止切換電流源時跳閘而引發(fā)測跳閘時間時異常!
,另外再加異常跳閘時的重試合閘和重上電復(fù)位。!
===================================================================
喂狗也是有講究的,不要在可能重復(fù)產(chǎn)生中斷的中斷服務(wù)函數(shù)里面喂狗。
萬一程序死翹翹了,但是中斷可不會死,這時候在中斷里面喂狗的話,程序就會在跑飛和中斷服務(wù)函數(shù)中切換
===================================================================
AD9里新建pcb時有時距離是三層板,導(dǎo)致布線時有些引腳怎么也不布的問題,刪掉pcb中間層即可
===================================================================
有的發(fā)生源有 2種讀電壓電流的接口,一種讀設(shè)定值,另一種讀實時值,判斷電壓到位沒要讀實時值那個接口。!
===================================================================
對 關(guān)閉電壓輸出 等高風(fēng)險的按鈕事件,為保證100%實時響應(yīng),則用類似以下的機制,即傳入第二個參數(shù)8,不然默認(rèn)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 = "請手動設(shè)置波特率到115200,然后點保存參數(shù)并重啟串口服務(wù)器!";
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");
}
}
===================================================================
如何調(diào)整WinForm界面ComboBox控件的高度
進擊的路飛桑 2020-07-06 14:27:23 636 收藏
分類專欄: # C#
版權(quán)
打開ComboBox控件的屬性頁,調(diào)整字體即可改變其高度
===================================================================
設(shè)備急停信號不要做成連續(xù)讀plc是否急停標(biāo)志位,而要做成2處:急停按鈕控制plc急停,還有上位機軟件里寫1個w區(qū)的位來急停
===================================================================
電流功率源的ict有些是發(fā)復(fù)位會強制斷下電流一下,有些是如果沒旁路則不會斷一下,如果瞬間斷一下源輸出值可能異常!
===================================================================
得做通訊使能:再上電穩(wěn)定8秒后才開始上線通訊,不然沒上電穩(wěn)定就不斷發(fā)通訊包,容易不穩(wěn)定
----
plc 別接地線,否則容易燒壞plc??
--
232轉(zhuǎn)485模塊的鐵外殼不要直接安裝在設(shè)備外殼上固定,需要加絕緣板,否則容易導(dǎo)致TXD燈常亮等問題
,其實不是模塊問題,而是485轉(zhuǎn)換模塊的 232輸入口的1號腳和5號腳這個gnd腳接反了,但是由于只用于發(fā)送而不接收,接反也能用,
但是不穩(wěn)定,偶爾txd燈常亮。。。。。。。
,所以對于只發(fā)送的通訊線路,出廠前要檢查下232線序。。
--
通信日志功能的重要性:
加了10工位的每個工位的通信日志功能后,可以看到有時清除斷路器電量發(fā)包時返回心跳包的結(jié)果內(nèi)容,說明心跳包和清除斷路器電量發(fā)包沖突了,從而不穩(wěn)定!。。
===================================================================
NO.0000:
比如重發(fā)包超時時間為80ms,而斷路器返回包的等待時間有100ms,則會在收到返回包前重發(fā)一個包,導(dǎo)致最終收到2個返回包,
而第2個返回包就干擾了后續(xù)發(fā)包的返回包,造成通訊紋亂:
所以:
所有包的重發(fā)包超時時間改大到1秒,原來只有120ms左右
,即加 if (isnowait == 0)//v1002:
{
//防止重發(fā)包的返回包干擾下面的續(xù)包:
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:
{
//防止重發(fā)包的返回包干擾下面的續(xù)包:
if (sleep_with_rt_ok(1 * 1000, gongwei_index, comm_index) == 1)
{
sended = 1;
break;
}
}
===================================================================
NO1。
所有textBox和checkbox, radioButton等全部要用拼音尾綴來命名,不能用數(shù)字尾綴來命令,否則容易
弄錯textBox,而導(dǎo)致隱藏的bug幾個月
-----------------
有很多按鈕的界面,可以把按鈕的事件做成一個,然后switch (button.Name)一下,比分散寫到各個事件函數(shù)里要方便:
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里如果復(fù)制某個界面的控件到新工程,那么所有輸入框事件和combox控件的改變事件都要手工拷貝來,不然很可能改值后保存不住,
而引發(fā)寫入?yún)?shù)不是改動的值,從而參數(shù)始終不變而改不了,誤以為寫成功了,比如跳閘閥值,這樣有安全風(fēng)險。。。
===================================================================
選擇性序列化:
[Serializable]
public class MyObject
{
public int n1;
[NonSerialized] public int n2;
public String str;
}
===================================================================
驗證斷路器等寫入的參數(shù)是否正確寫入時,緩存原始寫入值的變量最好帶絕對地址,否則按變量索引增量時容易出日后的兼容性問題:
//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) );
===================================================================
啟動主校準(zhǔn)進程之類后,再次開始時,除了判斷 是否校準(zhǔn)中 變量外,還要判斷這個主校準(zhǔn)進程是否isalive和running
===================================================================
像jieti的斷路器板子,校準(zhǔn)時 增益點和偏移點的給定電壓電流值必須為20%的比率關(guān)系,不然校不準(zhǔn)!
===================================================================
像今閏的三相源讀電壓電流等返回1失敗后,需要再重試2-4次。。。蝗徊环(wěn)定
===================================================================
ict報警旁路打開,可能不是產(chǎn)品和夾具的問題,可能是銅排沒有擰緊而接觸不良,也可能上銅排和下銅排同時接觸不良,都要擰緊才能解決
---
斷路器計量型的產(chǎn)品不同系列可能脈沖常數(shù)不一樣,導(dǎo)致脈沖誤差檢測老是50%誤差之類
===================================================================
同一時刻同時2個電壓源報警保護,很可能是源間回路有短路,比如換相開關(guān)和電流源的換相時的回路導(dǎo)致ab相間短路
,另外,如果電壓源老過載保護或IGBT保護而斷電,則可以串一個50歐到100歐左右的電阻,可以防止保護
===================================================================
機械手取料時料框里放個復(fù)雜圖案,機械手上裝攝像頭來模板識別這個圖案,獲得料框的xy偏移,也可以用于料分類的識別,
料框里用放料孔夾死料,來限位料,這樣不用視覺也可以準(zhǔn)確取料,
===================================================================
小220v繼電器換為大的220V交流接觸器,是否靈敏度降低?,從而自動在毛刺時不跳
===================================================================
寫串口等寫函數(shù)不要存在2個以上的線程在同時寫串口,即使是加了lock(obj)
,可以做成1個寫thread, 然后這個 thread里接收其他線程的發(fā)串口命令,來一個一個發(fā)包,
這樣可以防止比如心跳包的返回干擾其他包的返回,然后寫thread里2次寫間隔個20ms最好,
-------
最好做成執(zhí)行完幾步后進行一步校驗等,比如執(zhí)行完。。。。步后最后對比電壓電流值是否校準(zhǔn)通過,然后不通過時可以重試,這樣即使哪步丟包或錯誤,也可以最終自動修正,
這樣程序的穩(wěn)定性和魯棒性好點。!
===================================================================
移植老項目到新項目里時,務(wù)必先烤出來一份,再復(fù)制,防止改亂老的
===================================================================
有時候斷路器校準(zhǔn)完不準(zhǔn),不是校準(zhǔn)本身問題,還可能是電壓電流可能是BCD碼或解析異常,導(dǎo)致實際通訊過來的值不準(zhǔn)!
不要實時通訊監(jiān)測產(chǎn)品上線否,而要再產(chǎn)品上電完成后等待5秒左右后再開始通訊,防止剛通電不穩(wěn)定而亂寫初始化sn等
===================================================================
UT1616串口服務(wù)器的串口接收超時設(shè)置 時間不能大于50ms?? :需要比 send...發(fā)包函數(shù)里的 wait返回信號量 的延時值要小!
,不然容易一次收到2個包而導(dǎo)致校驗不通過?
===================================================================
雙倍誤差原理,漏電一直校驗不準(zhǔn):
不代入當(dāng)前漏電流值: _Current_LD;,否則因為_Current_LD不是實時的,從而導(dǎo)致漏電糾正的雙倍誤差!,所以還不如用漏電流設(shè)定值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不是實時的,從而導(dǎo)致漏電糾正的雙倍誤差!
-----------
不過以上也可能是 (byte)( (int)(Lou_innn)&0xff),(byte)(((int)(Lou_innn)>>8)&0xff) 里的漏電流float值轉(zhuǎn)為int值,比如29.999 ma,會自動變?yōu)?9ma,
而不是自動四舍五入而變?yōu)?30ma,從而漏電流精度有問題,而校不準(zhǔn)
===================================================================
c#用委托類似函數(shù)指針,這樣可以使2個類相互解耦,從而模塊化,提高代碼可閱讀性
===================================================================
所有參數(shù)做成拼音變量名等,不要寫死程數(shù)字,該抽象為函數(shù)的就新建立函數(shù),這樣:大改時可以批量搜索某個變量名來定位所有相關(guān)點而不會漏掉,從而防止bug
===================================================================
this.FormBorderStyle = FormBorderStyle.Fixed3D;//需要關(guān)閉按鈕, 不然卡死時關(guān)不了。!
===================================================================
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("只能運行一個程序!");
}
}
}
===================================================================
隨機性的不穩(wěn)定也可能是 參數(shù)最大值為65535,而設(shè)為900000等之類,導(dǎo)致參數(shù)實際為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;//設(shè)置模板
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;//設(shè)置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, "微斷死鎖狀態(tài)",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();
}
===================================================================
上層是個循環(huán),所以這里不能用 return;,需要 continue:
if (client1_To == null)
{
showmsg(ip1 + " toid not find!");
myClientSocket.Send(Encoding.ASCII.GetBytes("toid not find!"));
continue;//上層是個循環(huán),所以這里不能用 return;,需要 continue
}
===================================================================
有重發(fā)機制的返回包時最好不只用if ( rttobj.recv_ok1b == 1) 之類,而用當(dāng)前包的cur_cmd字符串經(jīng)由hashtable來分開置 recv_ok1b,這樣
可以防止上一個包的確認(rèn)包影響當(dāng)前包的recv_ok1b而漏包
,
或者用 if(recv_buf[2]==100&&cur_cmd="xintiaobao" )機制來判斷,但這容易永不成立???
===================================================================
以下里的if ( rttobj.recv_ok1b != 1) 可以刪掉,防止上一個包的確認(rèn)包影響當(dāng)前包的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;
}
}
}
--------------------------
===================================================================
有時候返回包了但功能沒執(zhí)行到位,可能不是因為其本身,而且啟動代碼有問題,導(dǎo)致已經(jīng)在工作的假象
===================================================================
讀參數(shù)到輸入框前先清空輸入框,防止本次讀失敗,而遺傳上次的輸入框值!!
===================================================================
發(fā)包函數(shù)里的發(fā)包狀態(tài)清零語句前最好加 System.Threading.Thread.Sleep(3);,防止下一個包影響上一個包的發(fā)包代碼后的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 = 0, byte headbyte = 0, string sub_cmd = "", int wait_sec_after_write=-1)
{
lock (lock_obj1[comm_lock_index])//bug????
{
lock (lock_obj2_recv_timeout)
{
rttobj.v16_buffer.Clear();//v80
rttobj.v40_cur_datastep = 0;//v400 !!!!!must
System.Threading.Thread.Sleep(3);//v500,防止下一個包影響上一個包的發(fā)包代碼后的if(recv_ok1b!=1.........
rttobj.recv_ok1b = 0;//v80
}
===================================================================
Program.cs里加入容錯機制,出錯時不奔潰,而顯示錯誤信息:
static class Program
{
public static void errlog1(string str)
{
str = str.ToLower().Replace("system.", "");//去掉特征串,防止看出是c#的
str = str.ToLower().Replace("windows.", "");
str = str.ToLower().Replace("form.", "");
MuNiuMa.lib.file_rw pw1 = new MuNiuMa.lib.file_rw();
string cont1 = "";
try
{
cont1 = pw1.read_a_file(System.Windows.Forms.Application.StartupPath + "/errlog1_G.txt");
}
catch
{
}
if (cont1.Length > 1024 * 400)
{
cont1 = "";
}
try
{
pw1.write_a_file(System.Windows.Forms.Application.StartupPath + "/errlog1_G.txt", cont1 + "\r\n\r\n" + str + "\r\n" + DateTime.Now.ToString());
}
catch
{
}
}
/// <summary>
/// 應(yīng)用程序的主入口點。
/// </summary>
[STAThread]
static void Main()
{
//處理未捕獲的異常
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
//處理UI線程異常
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
//處理非UI線程異常
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
string str = "";
string strDateInfo = "出現(xiàn)應(yīng)用程序未處理的異常:" + DateTime.Now.ToString() + "\r\n";
Exception error = e.Exception as Exception;
if (error != null)
{
str = string.Format(strDateInfo + "異常類型:{0}\r\n異常消息:{1}\r\n異常信息:{2}\r\n",
error.GetType().Name, error.Message, error.StackTrace);
}
else
{
str = string.Format("應(yīng)用程序線程錯誤:{0}", e);
}
errlog1(str + "系統(tǒng)錯誤002");
Form1.refwin1.libcls.MessageBoxw("系統(tǒng)錯誤002:"+str);
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
string str = "";
Exception error = e.ExceptionObject as Exception;
string strDateInfo = "出現(xiàn)應(yīng)用程序未處理的異常:" + DateTime.Now.ToString() + "\r\n";
if (error != null)
{
str = string.Format(strDateInfo + "Application UnhandledException:{0};\n\r堆棧信息:{1}", error.Message, error.StackTrace);
}
else
{
str = string.Format("Application UnhandledError:{0}", e);
}
errlog1(str + "系統(tǒng)錯誤003");
Form1.refwin1.libcls.MessageBoxw("系統(tǒng)錯誤003:" + str);
}
}
================================================================================================
USART_SendData(USART1, send_temp[ii]);
while(ccc1<1999999&&USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET){ccc1++;}
會少一字節(jié),而要:
while(ccc1<1999999&&USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET){ccc1++;}
USART_SendData(USART1, send_temp[ii]);
第一字節(jié)消失的解決方案:調(diào)用USART_SendData函數(shù)之前,先將‘TC’標(biāo)志位清‘0’就OK,
代碼如下
?
{
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_SendData(USART1, dat);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
?
================================================================================================
電壓輸出函數(shù)里加【int need_panduan_stop_jisozhuan_flag】參數(shù),如果按了軟件里的急停按鈕,則先調(diào)用輸出0電壓的函數(shù),然后所有jinrun_output_UI
里的if (need_panduan_stop_jisozhuan_flag == 1)的部分的調(diào)用全部直接return,來防止后續(xù)再意外輸出電壓
public int jinrun_output_UI(float U_out_val, float I_out_val, float UI_jiaodu,int need_panduan_jiaozhuan_ing,int need_panduan_stop_jisozhuan_flag)//UI_jiaodu=60度時功率因子為0.5
{
if (g_debug_is_ONE_PROD_mode == 1)
{
return 0;
}
if (need_panduan_stop_jisozhuan_flag == 1)//v400
{
if (stop_jiaozhuan_flag == 1 || is_jiaozhun_ing == 0)
{
return 0;
}
}
===================================================================
在調(diào)試stm8/stm32時,發(fā)現(xiàn)把單片機程序串口設(shè)置為8位數(shù)據(jù)位,偶校驗,上位機設(shè)置為8位數(shù)據(jù)位,偶校驗數(shù)據(jù)一直不對,當(dāng)把單片機程序改為9位數(shù)據(jù)位,偶校驗,上位機設(shè)置為8位數(shù)據(jù)位,偶校驗數(shù)據(jù)就對了,不知道為什么?是不是st單片機的一個bug
================================================================================================
用 序列化類來保存參數(shù)時,各參數(shù)類務(wù)必不要含類的引用,而要用類的my_ID形式來保存引用哪個ID的類,否則序列化后類的引用丟失!!
================================================================================================
刪除元素時務(wù)必在for (i。。。。掃描外再套層 while (ishave == 1),不然某些條件下會漏刪:
int ishave=1;//must 1
while (ishave == 1)
{
ishave = 0;
for (int ii = 0; ii < sim_UI_cls.all_yuanjian_links.Count; ii++)
{
if (sim_UI_cls.all_yuanjian_links[ii].yuanjian_id_form == pw_draw_shows.last_moveover_ele.yuanjian_id)
{
sim_UI_cls.all_yuanjian_links.RemoveAt(ii);
ii = 0; ishave = 1;
continue;
}
if (sim_UI_cls.all_yuanjian_links[ii].yuanjian_id_to == pw_draw_shows.last_moveover_ele.yuanjian_id)
{
sim_UI_cls.all_yuanjian_links.RemoveAt(ii);
ii = 0; ishave = 1;
continue;
}
}
}
================================================================================================
設(shè)計pcb時元件角度盡量一致,這樣制作貼片文件時簡單點,100個led燈以上時,led公共腳做成可選vcc或 gnd的,防止led貼反
================================================================================================
1.網(wǎng)絡(luò)等通訊時用 把整個 ini配置文件的字符串形式發(fā)出去,比用 | 號隔開每個項要好維護點。
2.串口或網(wǎng)口通訊可以先用 解析協(xié)議里的長度字串方式或求crc是否通過的試探方式,比【sleep 60 ms加在串口接收中斷開頭】要快,
如果解析協(xié)議方式受不到包,可以再用【sleep 60 ms加在串口接收中斷開頭】方式再收一次,即二合一方式:
而且不要用while(..){if ret_ok==1...形式,而要用
System.Threading.AutoResetEvent recv_ok1 = new System.Threading.AutoResetEvent(false)
和recv_ok1.waitOne(120); 和 rzecv_ok1.Set形式!!
--------------------------------------------------------
int is_sumok = 0;
int is_sumok2 = 0;
if (rttobj.v40_cur_is_dl1_or_68xieyi0 == 1)
{
if (is_DL_sum_ok(rttobj.v16_buffer.Count, rttobj.v16_buffer.ToArray(), gongwei_index) == 1)
{
is_sumok = 1;
}
}
if (rttobj.v40_cur_is_dl1_or_68xieyi0 == 0)
{
if (is_68_xieyi_sum_ok(rttobj.v16_buffer.ToArray(), rttobj.v16_buffer.Count) == 1)
{
is_sumok2 = 1;
}
}
int is_len_oked = 0;
if (v40_cur_datastep_flag == 1 || v40_cur_datastep_fla2g == 1)//v40_cur_datastep_flag==1指:需要立即 經(jīng)過if (rttobj.v16_buffer.Count >= rttobj.v40_cur_datalen)時
{
if (rttobj.v16_buffer.Count >= rttobj.v40_cur_datalen)
{
if (g_enable_sum_ok_log == 1)
{
MuNiuMaLib.fvdou_append_error_to_log_txtH("DL_68_sum_ok","rttobj.v16_buffer.Count="+rttobj.v16_buffer.Count.ToString()+", rttobj.v40_cur_datalen="+ rttobj.v40_cur_datalen.ToString()+ ", ok!,rttobj.v40_cur_is_dl1_or_68xieyi0=" + rttobj.v40_cur_is_dl1_or_68xieyi0.ToString());//for解決重合閘斷路器偶爾開上位機時通訊異常
}
is_len_oked = 1;
//rttobj.v16_recvbuf_len = rttobj.v16_buffer.Count;
//rttobj.v16_recvbuf = new byte[rttobj.v16_buffer.Count];
//rttobj.v16_buffer.CopyTo(rttobj.v16_recvbuf, 0);
//rttobj.v16_buffer.Clear();
//rttobj.v40_cur_datastep = 0;
//return 1;//return 1回來后TCPReadCallBack已經(jīng)BeginRead
}
}
if (is_len_oked==1&&(is_sumok == 1 || is_sumok2 == 1))
{
if (g_enable_sum_ok_log == 1)
{
MuNiuMaLib.fvdou_append_error_to_log_txtH("DL_68_sum_ok", "is_sumok == 1 ok!,rttobj.v40_cur_is_dl1_or_68xieyi0=" + rttobj.v40_cur_is_dl1_or_68xieyi0.ToString());//for解決重合閘斷路器偶爾開上位機時通訊異常
}
rttobj.v16_recvbuf_len = rttobj.v16_buffer.Count;
rttobj.v16_recvbuf = new byte[rttobj.v16_buffer.Count];
rttobj.v16_buffer.CopyTo(rttobj.v16_recvbuf, 0);
rttobj.v16_buffer.Clear();
rttobj.v40_cur_datastep = 0;
return 1;//return 1回來后TCPReadCallBack已經(jīng)BeginRead
}
}
================================================================================================
參數(shù)類和狀態(tài)類等最好和工位一起統(tǒng)一為1個類,比如:
如果重構(gòu),就把
cp_info_cls_info類和pw_addr_used_info類都寫到OP_with_com_name類里!!!
,然后地址固定為 1+工位號,或yike協(xié)議的:20+工位號 , 而不要用分配地址方式,地址固定為和工位號有關(guān)是最穩(wěn)定
================================================================================================
上位機要配mysql 綠色版,比 access穩(wěn)定多了!!
c#界面大字可以用textbox無邊框模式,
c#表格可以用 panel 的邊框模式,多個panel組裝成表格
================================================================================================
多次重試初始化聲卡,防止初始化失。
if (wm8978_Init()==0&&wm8978_Init()==0&&wm8978_Init()==0)//
{
================================================================================================
多線程里每次加一的id之類做增量緩存機制,只第一次加并存到緩存,后續(xù)讀緩存,防止多加了一次!
然后產(chǎn)品通訊下線時才清除此緩存
================================================================================================
以下可以防止點擊任務(wù)卡UI:
public delegate void p_void();
int ingsetrun = 0;
public void setrun(p_void p1)
{
if (ingsetrun == 1)
{
return;
}
ingsetrun = 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 = 0;
}
ingsetrun = 0;
}
p_void p1 = () =>
{
......................
};
setrun(p1);
================================================================================================
c#里:sleep 60 ms加在串口接收中斷開頭,或socket異步讀取時的開頭里的妙用,解決數(shù)據(jù)包的半包和粘包問題,加大比特率解決延時慢,
ping多線程卡串口接收,
有的設(shè)備有保存標(biāo)志,置位后才保存,
modbus3區(qū)寄存器不能用4區(qū)的命令來讀,
恒流源量程沒設(shè)對可能導(dǎo)致精度異常,
線程里多個函數(shù)調(diào)用,延時大而影響其他函數(shù)的實時性,應(yīng)多線程化調(diào)用各個函數(shù)。
發(fā)送函數(shù)可以加lock機制,一次只發(fā)送一個,防止并發(fā)沖突
chkintertert開電腦時可能一直失敗而不穩(wěn)定
歐姆龍plc監(jiān)視模式下hostlink才能寫變量,考慮模式設(shè)置能長時間保存不
-----------------------------------------
對于要多處來回拷exe調(diào)試的情況,可以集中一臺電腦來編程,其他電腦只 mstsc遠(yuǎn)程桌面式編程,編完后考慮exe到其他電腦,這樣可以防止源碼最新版本的版本混亂!!
,這樣也不好,可以用 seafile +服務(wù)端來同步源碼版本。。。梢栽O(shè)置覆蓋時的歷史備份數(shù)!!
================================================================================================
單片機控制步進電機的代碼里步進脈沖由timer發(fā)生時,加減速曲線時timer的頻率的控制有 hz1=hz1+step的每步加上一個量的方式會很穩(wěn)定而不丟步,
如果用hz1=hz_max*percent1的方式計算會丟步!!,
另外,用當(dāng)前左邊的絕對步數(shù)來輸出要運動的量比傳輸移動增量要穩(wěn)定!!,可以消除浮點不精確性的累計誤差。。
================================================================================================
20191207 :
5V等電源輸入時不要串聯(lián)防反接用的二極管,否則由于二極管壓降大和內(nèi)阻大,從而使電源續(xù)流能力和穩(wěn)定性大大下降,
從而導(dǎo)致各種干擾時的不穩(wěn)定性!
================================================================================================
74hc595等狀態(tài)鎖存芯片要定時main函數(shù)里刷新595芯片的狀態(tài)數(shù)據(jù),就像DRAM一樣刷新,否則受干擾容易出錯位
================================================================================================
stm32+mos管驅(qū)動開關(guān)電源變壓器時,是否在mos管輸入極前接個變壓器做信號輸入耦合,否則如果stm32一死機則導(dǎo)致mos處于
一直導(dǎo)通的高電平狀態(tài)而燒毀,加了變壓器則stm32輸出死機時則無信號給變壓器次級而保護住mos管
================================================================================================
timer定時器里判斷io引腳輸入的狀態(tài)計數(shù)來獲得io狀態(tài),比用main的loop循環(huán)里讀IO狀態(tài)要更實時,因為main可能執(zhí)行其他代碼而對輸入錯過
================================================================================================
c#代碼需要try 包住,對于c#調(diào)用c++的dll時,所有函數(shù)得加[HandleProcessCorruptedStateExceptions]來使c++出錯時不崩潰,而是進入c#的try catch
================================================================================================
x_1quan_puls=Tx_Buffer[90]+Tx_Buffer[91]*256+Tx_Buffer[92]*256*256;
而不能為 Tx_Buffer[90]+Tx_Buffer[91]*0xff+Tx_Buffer[92]*0xff*0xff
================================================================================================
u32 iiii;
u32 per_minnn=0;
printf("head_and_wav_len1:%d",head_and_wav_len1);
#define perwritenum 3000
iiii=0;
do{
per_minnn=perwritenum;
if(per_minnn>(head_and_wav_len1-iiii)){
per_minnn=head_and_wav_len1-iiii;
}
if(per_minnn==0){
break;
}
netconn_write(conn, request+iiii, per_minnn, NETCONN_COPY);
vTaskDelay(5);
iiii=iiii+per_minnn;
printf("netconn_write:iiii=%d,per_minnn=%d\r\n",iiii,per_minnn);
}
while(iiii<head_and_wav_len1);
比以下代碼好維護得多:
for(iiii=0;iiii<head_and_wav_len1;iiii=iiii+perwritenum){
if(perwritenum<head_and_wav_len1){
netconn_write(conn, request+iiii, perwritenum, NETCONN_COPY);
printf(" netconn_write ok....,iiii=%d ,write=%d\r\n",iiii,perwritenum);
}else{
netconn_write(conn, request+iiii, head_and_wav_len1, NETCONN_COPY);
break;
}
if((iiii+perwritenum*2)>head_and_wav_len1){
netconn_write(conn, request+(iiii+perwritenum), (head_and_wav_len1-iiii), NETCONN_COPY);
printf(" netconn_write ok 002....(head_and_wav_len1-iiii)=%d \r\n",(head_and_wav_len1-iiii));
break;
}
}
================================================================================================
c# winform程序里,注意:如果當(dāng)前顯示的是子窗口且主窗口隱藏,則關(guān)閉窗口后會exe還在進程管理器里,
需要子窗口再closing事件里把主窗口的is_ruuning設(shè)置為0
================================================================================================
SPI_FLASH_BufferRead(fft_model1, huanxingci_addr, NFREQ*Max_NFRAME*4);//
NFREQ*Max_NFRAME*4為100KB
,而最后一個參數(shù)類型為u16, 所以導(dǎo)致了讀取數(shù)據(jù)異常的奇怪bug
=========================================
有時keil打印浮點數(shù)時始終為0或其他異常,改為這個:
void PrintFloat(float value)
{
int tmp,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6;
tmp = (int)value;
tmp1=(int)((value-tmp)*10)%10;
tmp2=(int)((value-tmp)*100)%10;
tmp3=(int)((value-tmp)*1000)%10;
tmp4=(int)((value-tmp)*10000)%10;
tmp5=(int)((value-tmp)*100000)%10;
tmp6=(int)((value-tmp)*1000000)%10;
printf("f-value=%d.%d%d%d%d%d%d\r\n",tmp,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6);
}
=========================================
char 數(shù)組存儲float變量時,
浮點(FLOAT)轉(zhuǎn)換為CHAR
??float wTemp=3.3;
??
?char sBuf[4];
???char* temp;
???
memset(sBuf,0,sizeof(sBuf));
?
??temp=(char*)(&wTemp);
??
?sBuf[0] = temp[0] ;
?
??sBuf[1] = temp[1];
???
sBuf[2] = temp[2];
???
sBuf[3] = temp[3];?
CHAR轉(zhuǎn)換為浮點(FLOAT)
??char sBuf[4];
??sBuf[0]=0x33;
??sBuf[1]=0x33;
??sBuf[2]=0x53;
??sBuf[3]=0x40;
??float *w=(float*)(&sBuf);
???
---------------------
=========================================
c#里的thread函數(shù)里如有異步事件而無阻塞,則在執(zhí)行初始化后在末尾加 while(true){ Sleep(10);},防止線程退出后相關(guān)變量等被注銷
=========================================
原來只是把formmark窗體 hide了,現(xiàn)在是close:
是否沒close導(dǎo)致formmark窗體多個實例存在而異常??
=========================================
穩(wěn)定性=穩(wěn)定X穩(wěn)定X....,
所以如果網(wǎng)線通訊可靠,寧可用3個CPU+3個DM9000aep網(wǎng)卡+3個網(wǎng)口+1個路由器 形式來擴展,
也不要用 3個CPU+3個cpu間串口通訊或485通訊+1個DM9000aep網(wǎng)卡+1個網(wǎng)口 形式來擴展,
因為網(wǎng)口的可靠性已確認(rèn),多幾個也沒事,而串口通訊或485通訊穩(wěn)定性未知,比如大波特率下的可能的異常
=========================================
FMC_SDRAMInitStructure.FMC_SDClockPeriod改為FMC_SDClock_Period_3后播放網(wǎng)絡(luò)mp3時不死機了,說明要對sdram的線要手動布好,下次用等長線蛇形線!。。
FMC_SDRAMInitStructure.FMC_SDClockPeriod = FMC_SDClock_Period_2;
//FMC_SDRAMInitStructure.FMC_SDClockPeriod = FMC_SDClock_Period_3;
,
或者暫時補救的方法:SDRAM_GPIO_Config函數(shù)里把GPIO_Speed_100MHz改為GPIO_Speed_25MHz :
void SDRAM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure ;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE |
RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOI,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
=========================================
改數(shù)組的長度或malloc的分配長度時,注意先搜索這個變量名,在文件中查找全部,看這個指針變量的最大寫入長度,防止數(shù)組下標(biāo)溢出而死機,可以加超出長度的判斷和過濾
=========================================
01a1.程序做穩(wěn)定的秘訣1:所有要申請內(nèi)存的做到1個固定長度的數(shù)組里,用is_free來標(biāo)記是否可用,而用malloc在多線程時申請內(nèi)存太不穩(wěn)定
01a2.電路里多個用電模塊可以分開用幾個穩(wěn)壓芯片單獨供電,不要公用1個穩(wěn)壓芯片
01a3.降低fsmc總線頻率或SDRAM頻率,或sdio總線頻率或i2c總線頻率則可能解決一些不穩(wěn)定性故障
01a.異步方式的判斷某個io口的狀態(tài)時,可以用timer計時和記高電平數(shù)量等來做異步式的軟件式防抖算法!
0. 485工業(yè)總線通訊時,每次報文必須段,應(yīng)IP+cmd方式,報文段則錯誤率小,
0b.w25q256 等存儲器,一切讀寫前先初始化時記得重置cs腳為高電平,不能不管cs腳狀態(tài)
,
fatfs里 res=f_open(&ftemp,(const TCHAR*)main_script_outfname,FA_CREATE_NEW);沒有|FA_WRITE,則只能新建文件而無法寫入數(shù)據(jù)
0c.
stm32f407的flash寫入時,注意一個寫入范圍跨2個扇區(qū)時的問題,由于擦除前沒做緩存?zhèn)浞,所以寫入會?dǎo)致老數(shù)據(jù)丟失,
可以用
void STMFLASH_pre_earse(uint32_t save_addr,uint32_t number)
{
uint32_t StartSector,EndSector,i, save_addr_temp;
StartSector = GetSector(save_addr);
EndSector = GetSector(save_addr+number);
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
FLASH->ACR&=~(1<<10); //FLASH擦除期間,必須禁止數(shù)據(jù)fetch!!!搞了我兩晚上才發(fā)現(xiàn)這個問題!
for (i = StartSector; i<=EndSector; i += 8)
{
while (FLASH_EraseSector(i, VoltageRange_3) != FLASH_COMPLETE)
{
}
}
FLASH->ACR|=1<<10; //FLASH擦除結(jié)束,開啟數(shù)據(jù)fetch
FLASH_Lock();
}
來提前擦除,然后統(tǒng)一用不帶擦除的寫入函數(shù)來分步寫入
0.數(shù)據(jù)層和邏輯層分離,比如讀寫操作可以單獨在1個線程里,外部需要讀寫數(shù)據(jù)庫得可以做個消息隊列傳入數(shù)據(jù)層里一個個執(zhí)行 ,這樣就不會有數(shù)據(jù)庫鎖在高并發(fā)鎖死的問題和卡軟件的問題,還可以做讀寫分離,寫入數(shù)據(jù)時自動熱備份在多臺服務(wù)器的數(shù)據(jù)庫里,
0a.類似,相同的屬性和函數(shù)必須抽象為1個,集中在1個函數(shù)里,不要分布在每個母函數(shù)里,否則要改一個地方就得改所有調(diào)用的地方,牽一發(fā)而動全身
0b.可靠通訊里不要用if(packet_id!=cur_packet_id){......}等機制實現(xiàn)可靠性,而要用比如xy軸目標(biāo)坐標(biāo)步數(shù)是否等于上一次目標(biāo)步數(shù)的,另外上位機處理返回包的id時要判斷recv_id!=last_send_id是否相等,不相等則丟包(包發(fā)送函數(shù)為單獨線程,并取消息隊列,一次只發(fā)一個,這樣穩(wěn)定點)
機制實現(xiàn)可靠性,控制蜂鳴器的代碼也一樣:if(cur_to_beep_cc!=to_beep_cc){to_beep_cc=cur_to_beep_cc;....
而電磁閥等不需要滿足次數(shù)或步數(shù)等,可以直接丟包時不斷重發(fā)。
1.中斷和 main函數(shù)或其子函數(shù) 里不要同時有同一外設(shè)模塊信號的操作,如對74hc595的操作,對HT1621B的操作的信號線必須是有序的,而不能穿插,否則容易錯亂或花屏
2.中斷里標(biāo)志位變化時,回main函數(shù)或其子函數(shù)后盡量再判斷標(biāo)記位,否則容易導(dǎo)致遺傳上次標(biāo)志位的作用,而沒有及時發(fā)現(xiàn)中斷返回后標(biāo)志位已改變,而導(dǎo)致程序混亂
3.類似于信號量臨界區(qū)的穩(wěn)定性處理
4.對所有死等待如等待某標(biāo)志位 ,做超時退出處理,防止卡死
5.所有數(shù)組下標(biāo)盡量在訪問數(shù)組前都做下標(biāo)是否越界判斷
5b.做除零異常判斷,如果分母為零就不除
5c.注意:如果程序里有很多個定時器或多個高速外部中斷在工作,而且定時頻率都很高,那么如果?臻g分配很小,那么很容易發(fā)生嵌套的多層次搶占中斷而在進入中斷函數(shù)時發(fā)生局部變量分配儲存空間時失敗即堆棧溢出,而導(dǎo)致死機和卡死!,盡量
把功能用分支if做到1個定時器中斷里,少用定時器,并盡可能減小定時器頻率,并盡可能加大棧初始化空間,如stm32單片機里 .s 文件里的一些定義
6.對于一些潛在的不穩(wěn)定因素(甚至1-2個月才出現(xiàn)一次故障),比如網(wǎng)絡(luò)忙時包ID的先后次序發(fā)生變異,而導(dǎo)致if xxxx.id>old.id 永遠(yuǎn)無法再成立而卡死
7.多機通信盡量用單向通信,比如主從模式,主機發(fā)包輪詢查詢并從機返回結(jié)果模式,雙向的全雙工通信模式的穩(wěn)定性比較差
8.所有按鈕處理必須做按鈕去抖動算法,甚至不是阻塞式延時式的去抖動算法,而是狀態(tài)機時的異步流程方式的過濾掉按得過快的2次按鈕事件間隔,比如200ms內(nèi)再無按鈕事件且按鈕已沒按住才算按了1次,而且最好加個104電容在按鈕里并聯(lián)
9.多機主從通信盡量用 目的機ID+通信協(xié)議版本號+分包的子包ID序號(如1-10)+子包總數(shù)+本子包數(shù)據(jù)長度+上次包的全局ID+crc32校驗,方式來處理重復(fù)包,30ms內(nèi)沒收到從機反饋數(shù)據(jù)則重發(fā)包(比如重發(fā)4-6次)來處理丟包,并當(dāng)上次包的全局ID大于一定值時發(fā)清零包ID的cmd包來復(fù)位ID計數(shù),但要判斷從機先收到大點的ID的包的情況
10.網(wǎng)口通信比usb通信穩(wěn)定,usb在干擾時容易掉線
11.TF卡只適合做只讀的fat32文件系統(tǒng)應(yīng)用,如要常寫入,最好配合 W25q64來固定分片方式的無fat32文件系統(tǒng)式的寫入,因為TF卡常寫入,容易寫壞文件系統(tǒng)格式,比如意外掉電時等
12.對于一些串口接口的模塊,如果波特率用得太大,則單片機容易串口中斷響應(yīng)不過來,而發(fā)生嵌套的異常串口中斷,或丟串口數(shù)據(jù),導(dǎo)致通信不穩(wěn)定,波特率最好低點!
13.單片機程序的架構(gòu)最好做成異步式的狀態(tài)機式的虛擬多線程方式,不要用任何阻塞式的死等待和延時,延時也要做成狀態(tài)機里的某個狀態(tài)的delay的異步非阻塞式的累加,加到一定值才進入下1個狀態(tài)碼,異步方式比阻塞式的方式的穩(wěn)定性要好,處理實時性也高,多級菜單的屏幕顯示和按鍵處理也可以用狀態(tài)機式的異步流程,有個狀態(tài)碼指示當(dāng)前菜單的第1級選擇項號,選擇深度,第2級選擇項號。。。。
14.相同的類似的屬于同一功能概念下的處理過程最好集成到一個函數(shù)里做分支判斷,而不要把1個功能分散到很多個函數(shù)里,這樣代碼比較難維護和二次修改
15.可以抽象為1個功能或1個流程體內(nèi)的東西最好歸納法和抽象法抽象一下,不要做成if分支遍歷和窮舉所有情況方式,就像要做成矢量圖,而不是像素圖,抽象概括后的代碼寫法更有簡潔性,更
有可維護性和穩(wěn)定性!
15b.復(fù)雜度盡量做成可配置數(shù)組里的數(shù)據(jù),用配置數(shù)據(jù)來展開對代碼的控制,而不是把復(fù)雜度全部映射到代碼if分支等分支上,就像要做成矢量圖,而不是像素圖,就像一個圖形圓在可配置數(shù)組里(矢量圖里)是 yuan_type和sin cos,而若是代碼分支法,則是一堆關(guān)于圓的點陣
15c.按15點所說的抽象和歸納代碼后的代碼簡潔化和可維護性加大化,可以使代碼的條理和邏輯層次更加清晰,使更容易分析和預(yù)感到可能的bug和細(xì)節(jié)邏輯不嚴(yán)謹(jǐn),從而預(yù)防bug和不穩(wěn)定性bug,最終影響程序運行時穩(wěn)定性,所以bug是由思維決定的
16.看門狗在程序完工后最好加上去,軟件里的關(guān)鍵數(shù)據(jù)最好做RAM區(qū)多處備份,如備份10份,死機自動復(fù)位恢復(fù)后,檢查RAM區(qū)里備份里的各備份,重復(fù)次數(shù)最高的那幾份備份(且重復(fù)次數(shù)在6次以上)最可能為原始未被破壞的數(shù)據(jù),并做個ram標(biāo)志位檢查是上電復(fù)位,還是看門狗復(fù)位,復(fù)位后可以做不清除ram數(shù)據(jù),程序跑飛后,可以提前做些標(biāo)志位判斷,如跑飛到某處,可能某標(biāo)志位未被置位卻執(zhí)行到這,則可以自動判斷出是已跑飛,則自動軟件復(fù)位一次!
17.
單片機芯片的最近的外圍一圈上的單片機芯片vdd,gnd引腳附近必須多弄幾個0603封裝的貼片104電容來去干擾,pcb電源類走線的線寬必須要寬,至少0.4mm以上,甚至0.8mm以上,單片機芯片在pcb局部時盡量靠近電源芯片和供電芯片處
;
就單單才零點幾歐電阻的導(dǎo)線稍微長點來傳輸電源,比如20cm長,就會導(dǎo)致高頻雜波變多,所以導(dǎo)線末端務(wù)必加470uf以上的電解電容來去耦濾波,可以想象,在單片機引腳周圍1圈上也應(yīng)該最好加上幾個470uf電容和104電容環(huán)繞
;大功率pcb供電線走線千萬不要在單片機周圍走線或走過單片機,一定要把大功率電源的輸入接口做在大功率電路部分的附近,避免電源線的長距離走線!,要走也不要在單片機外周走過!,且大功率pcb供電線走線要邊走線邊1路隔個幾段距離就加個104去耦電容
18.
如果是軟件模擬spi信號接口或i2c接口,那么字節(jié)發(fā)送函數(shù)里最好先禁止總中斷使能,發(fā)完后再開啟總中斷使能,否則容易不穩(wěn)定
19.
c8051f340單片機芯片工作時如果電源電壓比較低或有突然的電源脈沖浪涌干擾,容易使燒好的程序丟掉,穩(wěn)定性好像不怎么好,stm32單片機芯片或GD32芯片好像就好點,就沒這個問題!,推薦優(yōu)先選后者
20.
對于以太網(wǎng)網(wǎng)絡(luò)模塊芯片應(yīng)用,最好把這芯片的RST復(fù)位信號接到單片機上,并RST腳接個104電容(有時候干擾容易導(dǎo)致意外硬式復(fù)位,而反而不穩(wěn)定),這樣,網(wǎng)絡(luò)卡死時可以強制復(fù)位網(wǎng)卡芯片一次來修復(fù)
21.
lwip等網(wǎng)絡(luò)協(xié)議棧的某些版本有可能有內(nèi)存泄露bug,或長時間運行后卡死,最好做定時自動軟件復(fù)位單片機功能,來防止內(nèi)存泄露過度
22.
芯片上如果flash夠,最好做從服務(wù)器查詢新版BIN固件來自動升級單片機自身的hex固件功能(固件下載完要做CRC32校對),以及usb的固件升級功能,即net bootloader和usb bootloader,來應(yīng)對【做項目即使完成和驗收后,幾個月出現(xiàn)的bug要修復(fù),而措手不及】
23.
比如 休眠后喚醒時給PB1引腳1個高電平后不明原因的死機,不一定是進入未定義中斷,而是main函數(shù)之前bootloader對PB1響應(yīng):主程序進入休眠態(tài),喚醒后 pb1為高,進入bootloader態(tài),但bootloader里沒有重新初始化stm32 cpu的時鐘系統(tǒng)等,故卡死,雖然bootloader后跳轉(zhuǎn)到的main里有重新初始化stm32 cpu的時鐘系統(tǒng)等的代碼,但是已執(zhí)行不到
24.
密腳芯片單片機等ic焊接時上松香吸錫后,如果用酒精清洗,是否過幾個月后會發(fā)生引腳間松香,助焊劑等物質(zhì)發(fā)生變質(zhì)而短路?而不穩(wěn)定?
25.
故障實時監(jiān)測功能很重要!(備災(zāi)1):比如某stm32版的帶網(wǎng)口的板子做個板子是否在線監(jiān)測和掉線時短信提醒功能,即每10秒鐘發(fā)送1個心跳包給服務(wù)器,如果服務(wù)器50秒內(nèi)沒收到某板子的任何心跳包,則此板子判為已掉線,并發(fā)送報警短信,email給對應(yīng)的技術(shù)員來通知處理和修復(fù)
26.
同一個安裝點上弄多個冗余板子,某塊故障時,另1塊自動切換到上線!,或雙電源,1個后備電池逆變電源,主電故障則自動切換到備電(備災(zāi)2)
27.
電源接插頭不要用圓孔的那種電源插座或usb接口插座,要用專用的接線端子母座和接線端子插頭(綠色方形的帶2針的那種,最好還帶上扣機制,插緊后自動上扣),否則電源不穩(wěn)或接觸不良,導(dǎo)致板子掉程序或其他異常,信號線也可以如此處理
28.
回流焊最后1段溫度曲線的達(dá)到最高溫度時如保持時間過長(推薦205度保持15秒,對于無鉛錫膏)導(dǎo)致錫膏被烤干,或錫膏過期了,就會導(dǎo)致出來后焊接效果不好,比如焊盤上錫的亮度不高,無光澤,太干燥,這時容易影響產(chǎn)品性能和加速日后的產(chǎn)品性能衰變
29.
eeprom寫入前不需要擦除整個扇區(qū),比如at24c02之類的,而單片機自帶的flash和w25q64等都是flash,寫入前如果不擦除整個扇區(qū),可能部分字節(jié)會寫錯字節(jié),而導(dǎo)致異常和不穩(wěn),或者做了擦除處理,但是寫某個數(shù)據(jù)時
未備份扇區(qū)里原數(shù)據(jù)而直接擦除扇區(qū),導(dǎo)致數(shù)據(jù)丟失,比如要寫入的數(shù)據(jù)數(shù)組跨越了2個扇區(qū)的邊界的情況
30.
盡量不要用一些外置的什么is62lv256+ls373方案的擴展ram內(nèi)存方式,而要直接用自帶高ram大小的192K ram的單片機,比如stm32f407或stm32f103zet,stm32103re ,gd32f103cb等,因為外擴方式容易不穩(wěn)定,比如受焊接質(zhì)量,打板精度,環(huán)境濕度溫度,電源波動等影響
而不穩(wěn)
31.
遞歸函數(shù)盡量用while/for循環(huán)+;驍(shù)組或鏈表的方式來替代,而不要做成多層次調(diào)用函數(shù)自身方式的遞歸,做也做成帶遞歸深度變量的,限制最大遞歸深度,否則容易堆棧溢出而死機
32.
當(dāng)要進行可能的后果極其危險或非常重要,不能被任何干擾誤觸發(fā)的控制操作時(比如房屋爆破隊驅(qū)動炸藥點火的裝置,自動駕駛的緊急剎車制動驅(qū)動器,高壓總閘開關(guān)繼電器),務(wù)必要備份10份以上的
控制碼,如果只有1份控制碼,當(dāng)這份控制碼被強干擾信號改變內(nèi)存數(shù)據(jù)而變?yōu)閷?dǎo)致觸發(fā)的目標(biāo)控制碼時,就會誤動作,而如果是10份,會校驗所有10份控制完全一致后再打開控制,這樣只有1份或2份被干擾
后就不會誤動作了,所有內(nèi)存區(qū)的任何變量也都實時備份10份以上,這樣被干擾后可實時恢復(fù)正確的數(shù)據(jù)(殘留下的重復(fù)次數(shù)最多的那幾份且重復(fù)次數(shù)在6次以上即為未破壞數(shù)據(jù)),
當(dāng)10份備份中重復(fù)次數(shù)在6次以下,意外著系統(tǒng)也無法自己自我修復(fù),就會自動進入崩潰狀態(tài)或自動軟式復(fù)位,
再進行重要控制或驅(qū)動前,也檢查所有內(nèi)存區(qū)的各變量的實時備份的10份數(shù)據(jù),如果任何一個變量的10份備份中各數(shù)據(jù)間重復(fù)次數(shù)在6次以下,則意外著干擾太強了,系統(tǒng)已錯亂,則不繼續(xù)進行重要控制或進行驅(qū)動。
,進行i/o控制的代碼最好在前面多加幾百行陷井代碼和判斷是否已被干擾弄崩潰的代碼,來盡可能減少意外直接跑飛到【進行i/o控制的代碼】的概率,或者不用任何簡單的i/o來控制重要設(shè)備或高壓的重要設(shè)備,
而用i2c或spi接口的外圍驅(qū)動芯片來間接控制,且i2c或spi必須為軟件模擬,在i2c或spi的發(fā)字節(jié)函數(shù)里,每發(fā)1個比特就再判斷一次【是否已被干擾弄崩潰即檢查所有變量的10份備份是否還算完整和一致】,
這樣就非常可靠和穩(wěn)定,不會被強干擾弄誤觸發(fā)了!
33.
在存在強電流,強功率,強電壓,強靜電,強射頻耦合傳遞,或可能有接觸式火花的地方盡量多的加光耦隔離來隔離信號控制計算處理部分和高壓信號驅(qū)動部分,如果一個系統(tǒng)上存在2個或以上獨立供電電源,
相互之間也必須完全隔離(GND等都不要接一起),用光耦耦合來傳遞控制,
繼電器盡量換為可關(guān)閉的晶閘管,或場效應(yīng)管,或其他大功率開關(guān)管,這些控制開斷時有【無火花】的優(yōu)點,防止繼電器通斷時的火花放電間隙導(dǎo)致的強干擾,如果實在要用繼電器,在繼電器的2觸點引出腳間接入RC去火花電路,
,并在繼電器的2觸點引出腳控制的真空電磁閥或電機等感應(yīng)負(fù)載上反向并聯(lián)入【1個高耐壓二極管+串1個0.5歐左右的大功率電阻】,以及并聯(lián)入1個體積大的104電容,來去除真空電磁閥或電機等在斷電瞬間產(chǎn)生的
反向電動勢,從而消除反向電動勢在繼電器觸點剛斷開時的微小間隙里產(chǎn)生火花,從而消除火花產(chǎn)生的強干擾來使單片機跑飛,死機或復(fù)位的后果,
,另外如果用繼電器,最好每個繼電器的VCC串1個10歐的電阻后再控制繼電器,這樣繼電器吸合時對電源的脈沖干擾小點!
34.
做編程項目或電子項目,務(wù)必做一個 發(fā)現(xiàn)的未解決的程序bug的備忘.txt ,及時記錄發(fā)現(xiàn)的偶爾出現(xiàn)的bug,和突然靈感里閃現(xiàn)的預(yù)感到的可能會出現(xiàn)的bug情況,以及要改進的地方,
比如一些操作不變雞肋問題,穩(wěn)定性問題,都要及時記錄,因為如果每忘記1點點bug,都意味著可能日后會出現(xiàn)這個bug,即導(dǎo)致項目的徹底失敗,畢竟【一個項目的成功是靠堵住所有一些可能的細(xì)節(jié)蟲洞和雞肋來做到的】
35.
如果是在別人的開放平臺或接口上做程序,那么需要定期檢查所有情況都是否正常,比如即使今天全部正常,但是他們平臺也許早就換了接口版本,只是老用戶還是維持老版本接口,新訂購的用戶用我們isv軟件時卻已切換到新接口,不能用,卻發(fā)現(xiàn)不了
36.
字符串?dāng)?shù)組即二維的uchar數(shù)組,在定義時,前1個方括號索引字符串序號,后1個方括號索引當(dāng)前字符串的字符下標(biāo),比如u8 wavfile1n[7][15] 代表7個最長14個字符的字符串,方括號別弄反了,容易異常
37.
板子上電源處加的電解電容在1000uf以上時,容易上電時電容瞬間電流過大而導(dǎo)致開關(guān)電源發(fā)生短路保護,如此循環(huán),而不易啟動
38.
EMI:pcb電源輸入部分增加共軛電感器抑制中高頻的共模噪聲,并加共模濾波電容(2個電容串聯(lián),2端接電源正負(fù),中間點接地)和差模濾波電容
39.
如果要抗雷擊浪涌,應(yīng)在電源輸入處并聯(lián)入動作電壓大于額定電壓2-3倍以上的壓敏電阻,并且電源輸入處pcb走線寬度要粗,并電源輸入處的壓敏電阻前的走線串聯(lián)入玻璃保險管的座的相關(guān)元件
40.
pcb信號線的走線盡量短而且寬: pcb布線時選擇合理的導(dǎo)線寬度 由于瞬變電流在印制線條上所產(chǎn)生的沖擊干擾主要是由印制導(dǎo)線的電感成分造成的,因此應(yīng)盡量減小印制導(dǎo)線的電感量。印制導(dǎo)線的電感量與其長度成正比,與其寬度成反比
晶振走線必須盡量短: 時鐘線、信號線也盡可能靠近地線,并且走線不要過長,以減小回路的環(huán)面積
信號線走線拐角應(yīng)采用圓弧形: 電路板上的印制線寬度不要突變,拐角應(yīng)采用圓弧形,不要直角或尖角。(9)時鐘線、信號線也盡可能靠近地線,并且走線不要過長,以減小回路的環(huán)面積。
保持環(huán)路面積最小,降低干擾對系統(tǒng)的影響,提高系統(tǒng)的抗干擾性能。并聯(lián)的導(dǎo)線緊緊放在一起,使用一條粗導(dǎo)線進行連接,信號線緊挨地平面布線可以降低干擾。電源與地之間增加高頻濾波電容
采用完整的地平面設(shè)計,采用多層板設(shè)計,鋪設(shè)地層,便于干擾信號泄放。
41.
密腳芯片懷疑存在連焊時的處理技巧1:拖焊時務(wù)必放很多松香到吸錫銅絲線上,否則容易吸錫不徹底,而留下ic引腳間不穩(wěn)定性的短路問題,最后用99%濃度(不能含水多)的酒精擦去殘留的松香,并再烙鐵順引腳刮下來清理一遍酒精等
42.
盡量用全局變量或全局?jǐn)?shù)組變量替代局部變量,因為進入函數(shù)時要不斷的分配局部變量,容易影響執(zhí)行效率和耗用完堆?臻g等;
43.
電源部分的正常和穩(wěn)定(比如電壓波紋小,高頻噪聲濾除率高,功率實足,抗突變的浪涌大電流能力強)是單片機電路和控制電路穩(wěn)定工作的最重要的前
44.
像LM1086-3.3或LT1086-3.3,LMXXXXX....之類的貼片式三端穩(wěn)壓器,在貼片工藝完后并過回流焊爐后,由于焊盤上印刷錫膏時錫膏量不足或者三端穩(wěn)壓器引腳上翹會導(dǎo)致三端穩(wěn)壓器過回流焊爐焊接后存在虛焊或接觸不良,
所以應(yīng)該每次都手工補焊下來補點錫,否則容易不穩(wěn)定,或在電子產(chǎn)品運輸工程中脫焊而開路
sot23封裝的貼片三級管也容易引腳上翹而導(dǎo)致回流焊的焊接質(zhì)量不良而導(dǎo)致開路或虛焊(如在運輸過程中振動而脫焊)
45.
所以出廠前的24h老化測試和pcb帶電工作的高強度振動測試也很重要,來檢測是否有虛焊的現(xiàn)象(如在運輸過程中振動而脫焊)
46.
電源開關(guān)等不要用機械式的單刀雙擲式的那種微型撥動開關(guān),很容易損壞或接觸不良,最好做成自鎖式的那種按鈕式電源開關(guān)或船型開關(guān)
47.
注意場效應(yīng)管的前置驅(qū)動電壓放大級用的三極管的發(fā)射極最好是接地形式的而場效應(yīng)管G端接三極管的集電極再1個上拉電阻上拉到比如24V,否則如果NPN的發(fā)射極接G端時,當(dāng)三極管導(dǎo)通,
發(fā)射極的電壓最大也只有基極的電壓的大小,將無法良好的使場效應(yīng)管完全導(dǎo)通,比如IRF540N的Vgs需要10V左右才完全導(dǎo)通,另外應(yīng)該在G端對地并聯(lián)1個10V穩(wěn)壓管,因為一般Vgs也不能太大,20V左右最大了
不要用單片機IO口直接驅(qū)動場效應(yīng)管,因為一般場效應(yīng)管Vgs在10V左右才能完全導(dǎo)通和飽和,單片機io口電壓不夠,會驅(qū)動不良
48.
光耦存在壽命,所以盡量用非光耦的取代元件來做隔離,比如一些ic:
光耦中的發(fā)光二極管隨著時間推移而老化;比如開關(guān)電源反饋環(huán)路中光耦老化,傳輸比下降后可能引發(fā)故障。
不能用線性光耦作為模擬量傳感:通光耦產(chǎn)品手冊中對電流傳輸比只給出一個大概的范圍(從低到高可以差別一倍),而且沒有給出溫漂和老化的指標(biāo)。用于開環(huán)傳輸模擬量,精度是得不到保證的,所以導(dǎo)致過壓、過流,以致?lián)p壞
49.
74hc595的時鐘和數(shù)據(jù)線上要加阻容濾波的 這類IC 速度太高 對毛刺干擾特別敏感,而導(dǎo)致誤動作或屏幕錯亂
49a.
做電壓檢測電路時,比如測220V電壓或380V電壓,不要用線性光耦+1M電阻來降壓采樣,而要用微型工頻變壓器(鐵芯那種(非高頻磁芯))來降壓并整流采樣,因為光耦會老化和被擊穿(雷擊等意外),或有溫飄
50.
不要在任何中斷函數(shù)里調(diào)用1個需要等待另1種中斷函數(shù)來置位目標(biāo)所需標(biāo)志位才能結(jié)束的函數(shù)或代碼段,否則可能因為中斷優(yōu)先級的關(guān)系而導(dǎo)致這另1種中斷函數(shù)永遠(yuǎn)無法執(zhí)行而卡死在等待里,導(dǎo)致死機
51.
謹(jǐn)慎使用keil自帶的庫函數(shù)之sprintf和printf,容易出問題,比如卡死或內(nèi)存泄露
52.
LQFP64/LQFP48之類的密腳IC如果用拖焊法手動焊接,那么如果芯片在pcb上的每個引腳的焊盤的長度如果比較長,則很容易拖焊后在引腳的焊盤的靠芯片內(nèi)部方向處產(chǎn)生錫的殘留而導(dǎo)致2個引腳焊盤間的短路,所以
此法不穩(wěn)定,還是用鋼網(wǎng)刷錫膏+回流焊法比較可靠!
53.
ad采樣均值濾波法時應(yīng)該用:
chindex=0;
memcpy(&ch_vout1_lvbo_temp[0],&ch_vout1_lvbo[chindex][1],(junzhi_lvbo_count-1)*sizeof(float));
memcpy(&ch_vout1_lvbo[chindex][0],&ch_vout1_lvbo_temp[0],(junzhi_lvbo_count-1)*sizeof(float));
ch_vout1_lvbo[chindex][(junzhi_lvbo_count-1)]=read_adc_value();
if(ch_caiyang_alled[chindex]==0){
ch_vout1_lvbo_index1[chindex]++;
if(ch_vout1_lvbo_index1[chindex]>=(junzhi_lvbo_count)){
ch_caiyang_alled[chindex]=1;
}
}
這樣的代碼機制,而不是插滿一次緩沖池后從頭開始覆蓋,而是不斷往前移1格來不斷的從緩沖池的末尾插入,而且memcpy時注意是(junzhi_lvbo_count-1)*sizeof(float)個字節(jié),而不是(junzhi_lvbo_count-1)個
54.
數(shù)字電源或開關(guān)電源的mos管的驅(qū)動級的信號輸入最好帶一級最大高電平時間限制的硬件式電路,比如最大允許高電平500us,來防止卡死時一直高電平而使mos一直導(dǎo)通而燒壞mos或高頻變壓器!
55.
注意臨界大小判斷時容易出現(xiàn)的bug:
if((ch_vout1_zhankongbi_value1+diff_v1)<=max_zhankongbi1){
ch_vout1_zhankongbi_value1=ch_vout1_zhankongbi_value1+diff_v1;
}
,這里,如果diff_v1很大,但是ch_vout1_zhankongbi_value1+diff_v1剛好只比max_zhankongbi1大1,那么就會陷入死循環(huán),永遠(yuǎn)無法達(dá)到max_zhankongbi1,而且不是只離max_zhankongbi1一點,容易使調(diào)節(jié)環(huán)在誤差比較大時卡死
,所以要加else來改為:
if((ch_vout1_zhankongbi_value1+diff_v1)<=max_zhankongbi1){
ch_vout1_zhankongbi_value1=ch_vout1_zhankongbi_value1+diff_v1;
}else{
ch_vout1_zhankongbi_value1=max_zhankongbi1;
}
56.
數(shù)字電源的每次反饋調(diào)整pwn或每次pid調(diào)整時的調(diào)整間隔周期必須在相隔500us左右以上而不是1個while死循環(huán)里不停的調(diào)整,
而要用timer定時定間隔的調(diào)(所以輸出級的電解電容務(wù)必2200uF以上,來延長電壓突變緩沖時間),而且每次調(diào)整的誤差比例乘積因子必須要小,否則容易輸出電壓波紋很大!
控制輸出電壓波紋的關(guān)鍵是這500us內(nèi)不斷的采樣adc(可多次DMA式adc)來累加并在500us結(jié)束時取累加值的平均值來數(shù)字濾波,因為一般ad采樣電壓的波紋比較大
57.
keil中 3.11/6.1會被當(dāng)做小數(shù)來計算結(jié)果,如果是3/23,則結(jié)果為0,因為被當(dāng)做整數(shù)來處理,所以必須寫為(float)3.0/(float)23.0
58.
如果需要動態(tài)改變單片機定時器的定時周期,那么要注意周期定時值不能被改的太小,否則會導(dǎo)致不斷的高速的進入定時中斷而卡死,要判斷和限制下
59.
【bug調(diào)試和發(fā)現(xiàn)bug原因,解決問題現(xiàn)象】的原則是【在相對系中尋找局部絕對系來作為支撐點,進而慢慢全部覆蓋問題機制】,比如找?guī)讉確定點和可以差量法,對比法確定的程序現(xiàn)象和機理,然后再慢慢消元,減少變化量的個數(shù),盡量避免多個量
一起在變的問題框架,因為變量1多,要測試的樣本就成指數(shù)級增長!
60.
c#上位機程序防卡頓經(jīng)驗1:
阻塞式的任務(wù)和代碼要全部在后臺線程里獨立運行,而不能在主界面的主線程里執(zhí)行,對于后臺線程里要跨線程操作主界面控件的,這段操作界面的代碼要用到Invoke,相當(dāng)于卡死界面一點時間,所以在
Invoke里調(diào)用的 Action<string> actionDelegate3 = (x) =>{。。。。}之類的里的代碼盡量執(zhí)行時間耗時極短,比如盡量把界面控件某狀態(tài)的死等待代碼不要放在Invoke的執(zhí)行體代碼里,
比如 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }這段等待webbrowers加載網(wǎng)頁完畢的代碼不要放在Invoke的執(zhí)行體代碼里,而放在
線程代碼里,即Invoke的執(zhí)行體代碼執(zhí)行完后的緊接著的下一句代碼再執(zhí)行 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }
,而Invoke的執(zhí)行體代碼里如果執(zhí)行webbrowers1.Navigate(aaa1.url);之類的,也會在打開網(wǎng)頁時卡頓一段時間界面,所以盡量改為異步方式的Navigate函數(shù),比如BeginNavigate()之類的
61.
C#里等待webbrowser是否加載網(wǎng)頁完成的穩(wěn)定可靠的檢測方法為:延時式再判斷累計法(注意:線程里讀webbrowser的Isbusy值時要用Invoke,否則容易出錯):
int kongxianed = 0;
while (true)
{
System.Threading.Thread.Sleep(100);
bool isbusy1=true;
Action<string> actionDelegate3a11 = (x) =>
{
isbusy1 = webb1.IsBusy;
};
webb1.Invoke(actionDelegate3a11, "");
if (isbusy1 != true)
{
kongxianed++;
}
else
{
kongxianed = 0;
}
if (kongxianed > 20)//如果2秒內(nèi)都無Busy狀態(tài)再出現(xiàn),那么極有可能網(wǎng)頁和其所有網(wǎng)頁子框架都已加載完成
{
break;
}
}
62.
c#里容易異常的地方和一些界面控制代碼外圍盡量包1層try ...catch,來防止崩潰出錯
63.
c#里object 不能直接隱式轉(zhuǎn)為int,而要先tostring()后再int.parse?
64.
js里的ajax里提交后盡量加&r="+Math.random()后綴來防止緩沖而不更新
65.
路徑可能變動的網(wǎng)頁里的帶背景img定義的css盡量不要放在網(wǎng)頁里,而放在一個外部css文件里,這樣圖片可以相對css文件來定義路徑
66.
對于ajax點擊鏈接執(zhí)行函數(shù)但不跳轉(zhuǎn),建議不要用href:void(0)...+onclick(兼容性不好,有些瀏覽器異常打開新窗口),而用div+onclick+div加手型鼠標(biāo)css
,而且onclick所在div要顯式定義好height和width,否則容易兼容性不好,而有的瀏覽器點擊無響應(yīng)!
Example:CSS鼠標(biāo)手型效果 <a href="#" style="cursor:hand">CSS鼠標(biāo)手型效果</a>
Example:CSS鼠標(biāo)手型效果 <a href="#" style="cursor:pointer">CSS鼠標(biāo)手型效果</a>
注:pointer也是小手鼠標(biāo),建議大家用pointer,因為它可以兼容多種瀏覽器。
67.
js里或帝國cms里esetcookie('last_xianhua'.$pinlun_id,$time1,time()+3600,0);的設(shè)置cookie超時時間,是按絕對值超時到期時間,而不是按相對的到期時長
68.
c# webbrowser模擬提交表單時,
refwin.Focus();
webBrowser1.Focus();
get_elem("input", "", "", "", "id", "J_payeeShowAccount").Focus();
SendKeys.SendWait(arr1[uidto_index].Trim());
get_elem("input", "", "", "", "id", "J_payeeShowAccount").SetAttribute("value", arr1[uidto_index].Trim());
try
{
get_elem("input", "", "", "", "id", "J_payeeShowAccount").RaiseEvent("onchange");
}
catch
{
}
,其中用SendKeys.SendWait發(fā)送1次模擬按鍵是為了觸發(fā)input輸入框的onchange,而最終還是強制用.SetAttribute("value", arr1[uidto_index].Trim());來正確的設(shè)置值,防止SendKeys.SendWait發(fā)送時丟字符(比如界面很卡時或手動轉(zhuǎn)移輸入焦點就容易丟)
,即SendKeys.SendWait+ .SetAttribute("value", 結(jié)合法
69.
webbrowser模擬輸入和點擊要1個1個字符間隔著輸入,且要模擬onmousemove事件:
public void SendWait22(string ss, int per_ms)
{
for (int i = 0; i < ss.Length; i++)
{
System.Threading.Thread.Sleep(per_ms);
SendKeys.SendWait(ss[ i].ToString());
}
System.Threading.Thread.Sleep(per_ms);
}
Action<string> actionDelegate3a22qq112a = (x) =>
{
for (int i = 0; i < 20; i++)
{
try
{
webBrowser1.Document.Body.RaiseEvent("onmousemove");
}
catch { }
System.Threading.Thread.Sleep(10);
}
};
webBrowser1.Invoke(actionDelegate3a22qq112a, "");
70.
各項都改好和文件都復(fù)制好了,還是故障依舊,那么很可能問題不在文件本身,而在文件的訪問權(quán)限給對應(yīng)的服務(wù)進程沒賦予權(quán)限,比如:php怎么也加不入mysql組件,php.ini和庫,dll擴展都復(fù)制了也一樣,此時很可能是system32和外面的php.ini,庫等的安全屬性沒賦予iis賬戶和network等
71.
穩(wěn)定性還含過熱保護穩(wěn)定性:如果pcb設(shè)計上含帶過熱保護的大電流芯片,比如THB7128或者一些開關(guān)電源的過熱保護,所以出廠前要設(shè)置最合適的THB7128工作電流來使連續(xù)工作1周或更長都不會發(fā)生過熱保護而罷工
72.
對于大功率的處理器或單片機,比如DSP,ARM9-11等,發(fā)熱量很大的,那么必須加風(fēng)扇散熱,否則cpu的高溫度會導(dǎo)致運行速度下降和更容易程序跑飛,從而導(dǎo)致死機和崩潰。罕热缫恍┞酚善鞯乃罊C很可能是散熱不好,沒加風(fēng)扇,或電源部分用的老式的變壓器式
的低輸出電流且容易受電網(wǎng)電壓欠壓影響輸出電壓的電源,而不是強悍低波紋且穩(wěn)壓范圍廣的開關(guān)電源
優(yōu)化散熱+自動重啟自身(用于解決可能的內(nèi)存泄露等)可徹底穩(wěn)定路由器:
大多數(shù)的路由器散熱都做得不是太好,尤其是CPU;
我的一個N年前的NW618 經(jīng)過改造后,連續(xù)無障礙運行了4、5年了;
首先是主要發(fā)熱部件的散熱,車間里找銅皮、銅板——最好是紫銅,然后做好防短路處理,手工折好形狀——也是個稍微考驗?zāi)X子的活,最好是高度剛好在扣上蓋后能牢牢壓住;
NW618可以刷固件,刷了個番茄的;
設(shè)置定時自動重啟;
別管多熱,這個路由器都沒給我填過麻煩;
路由器、貓 絕大多數(shù)的散熱都很差,所以,提醒各位注意降溫;
我是拆了3個以上的無線路由器后才發(fā)現(xiàn)的…… ;
73.
div+css模板里div塊間的分界html注釋是div里的table化,有利于節(jié)約閱讀情緒成本
74.
伺服電機速度響應(yīng):加速區(qū)才幾ms而一般步進要100-200ms+最好用柔性聯(lián)軸器(因為加速區(qū)時扭矩很大)
75.
伺服電機套裝的伺服電機簡易買無刷的3線接口的直流無刷電機 ,有刷的電機不耐用
76.
數(shù)碼管熄滅時再做ADC,比如4位數(shù)碼管顯示,分5個時間隙,假如2ms一位,則前面8ms顯示,然后關(guān)閉顯示2ms后做ADC,避免LED電流對ADC的影響。外掛的ADC是因為LED的電影響不到它。
ADC部分沒有獨立的模擬地和電源的MCU,就要這樣考慮。
77.
實踐證明:smt中,貼片式TF卡座過回流焊后很容易虛焊(焊腳上翹現(xiàn)象),所以每次必須手工補焊一次
78.
實踐證明:部分旋擰方式的可調(diào)電阻內(nèi)部有些是開路的,壞的,必須出廠測試
79.
不用【多層if或[多個單層if+return]】實現(xiàn)逐層條件判斷的方法:do{ if(...){ break;} if(...){ break;} if(...){ break;} if(...){ break;}...}while(0);
80.
注意: RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE); 不小心弄成 RCC_AHBPeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE); 或RCC_APB2PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE)的低級錯誤!!
81.
#define configKERNEL_INTERRUPT_PRIORITY 255 //freertos任務(wù)切換調(diào)用中斷的優(yōu)先級必須最低,防止實時任務(wù)嵌套中斷被任務(wù)切換中斷所打斷而異常。!
82.
freertos里的
#ifndef traceMALLOC
#define traceMALLOC( pvAddress, uiSize )
#endif
#ifndef traceFREE
#define traceFREE( pvAddress, uiSize )
#endif
可以用于內(nèi)存泄露的內(nèi)存分配和回收的對稱性檢測,lwip里應(yīng)該也有類似的機制
83.
freertos里采用heap_4.c這個內(nèi)存管理方案來編譯進:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 20 * 1024 ) ) //采用heap_4.c才有此選項,即不交給keil的malloc函數(shù)來分配內(nèi)存,而由系統(tǒng)管理
84.
如果數(shù)據(jù)傳輸要不斷對新連接分配臨時內(nèi)存區(qū),那么不定長的分配的和回收而留下內(nèi)存空間間隙的話,久而久之會導(dǎo)致太多的內(nèi)存碎片,是否最后導(dǎo)致性能越來越慢,甚至內(nèi)存分配失敗而卡死?,如lwip?,所以單片機內(nèi)存分配簡易采用數(shù)組劃分定長分配內(nèi)存區(qū)的策略才穩(wěn)定!
,配合configMINIMAL_STACK_SIZE設(shè)置夠大來使定長分配內(nèi)存,而減少內(nèi)存碎片積累
85.
Heap_Size EQU 0x00010000 ;64k作為堆大小,即用于freertos的malloc分配內(nèi)存,來使http_connect_task連接不會內(nèi)存分配失敗,配合configMINIMAL_STACK_SIZE設(shè)置夠大來使定長分配內(nèi)存,而減少內(nèi)存碎片積累
86.
總線頻率要留有余量,比正常能驅(qū)動外設(shè)的最高頻率還稍微低點,才能穩(wěn)定:
FSMC_NORSRAMTimingInitStructure.FSMC_CLKDivision =12;//2分頻的話驅(qū)動dm9000a不起來,總線頻率太快!,降為12分頻就可以
87.
如果非要用usb轉(zhuǎn)串口方案,穩(wěn)定性和兼容性比較:FT232>PL2303雙芯片>ch340,如何查某種usb轉(zhuǎn)串口線所采用的芯片型號?:設(shè)備管理器里查設(shè)備屬性的VID和PID法
88.
有些以太網(wǎng)網(wǎng)絡(luò)芯片要做斷線檢測并自動重連,否則很容易斷線后徹底卡死無法收發(fā)數(shù)據(jù),比如dm9000aep:
int_status = dm9000_io_read(DM9000_ISR); /* Got ISR */
dm9000_io_write(DM9000_ISR, int_status); /* Clear ISR status */
//Check link state
if((int_status & ISR_LNKCHG)!=0)//掉線檢測并重連
{
int_status = dm9000_io_read(DM9000_REG_NSR);
if((int_status & NSR_LINKST)==0)//掉線檢測并重連
{
rt_dm9000_init();
dm9000_io_write(DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);
return (0);
}
}
89.
注意單句編程語句寫法和次序不同而導(dǎo)致的穩(wěn)定性不同,如:
int_status = dm9000_io_read(DM9000_ISR); /* Got ISR */
//Check link state
if((int_status & ISR_LNKCHG)!=0)//掉線檢測并重連
{
dm9000_io_write(DM9000_ISR, ISR_LNKCHG); /* Clear ISR_LNKCHG ISR status */
...........
比
int_status = dm9000_io_read(DM9000_ISR); /* Got ISR */
dm9000_io_write(DM9000_ISR, int_status); /* Clear all ISR status */
//Check link state
if((int_status & ISR_LNKCHG)!=0)//掉線檢測并重連
{
...........
要好!
90.
c#等對于一些多線程用的組件,在線程主入口函數(shù)里的末尾務(wù)必加while(true){sleep(10);}之類的死循環(huán)來卡住,否則 線程主入口函數(shù)里新建立的子線程會因為線程主入口函數(shù)返回了而沒卡住,而在不久后會全部注銷子線程和變量,而導(dǎo)致一些運行一段時間后莫名其妙的exe退出現(xiàn)象!
91.
if((args.data[1]&128)==128)與 if(args.data[1]&128==128)可能有區(qū)別,因為可能==優(yōu)先于邏輯與&,所以 if(args.data[1]&128==128)可能會變?yōu)?nbsp; if(args.data[1]&(128==128)),即 if(args.data[1]&1)
92.
按照我做開關(guān)電源的經(jīng)驗,八成是變壓器出問題了,一個開關(guān)電源里面的所有器件的參數(shù)都比較可控,比如耐壓、耐流、功率、容值、阻值等等,唯一難以把控的來料就是變壓器,每批的參數(shù)都會有偏差,找找沒炸雞的電源,測試一下MOS上面的尖峰電壓吧,最有可能就是變壓器漏感超標(biāo)導(dǎo)致尖峰電壓過高擊穿MOS,造成連串的燒毀!:換更高耐壓的mos管才是根本解決辦法,比如IRF540N換為IRF640N
93.
程序防破解的原理:【可利用的特征的消滅技術(shù),加大破解時間成本】:運用【變中有變】原理:在原程序正常功能的變量變化鏈之中再套入1層變化,而這層變化是防破解用的,破解者無法識別是正常變化還是防破解用的變化,除非理解了我的程序,但是如果有理解能力,自己都能寫代碼,對這個矛盾加以利用即可:
,如正常代碼功能里對uuu[1]的值進行判斷和分支,但是我們?nèi)绻R別到軟件未注冊,那么就可以在判斷未注冊后,不做簡單的返回,而是對uuu[1]的值取些非正常分支判斷需要的值
94.
數(shù)字電源板開關(guān)頻率可以設(shè)到36khz,不要15khz,15khz容易在負(fù)載功率波動時輸出電壓不穩(wěn)!,而64khz又容易使輸出級整流管過熱!
95.
不要在中斷函數(shù)里做網(wǎng)口發(fā)包 ,usb發(fā)包,串口發(fā)包等數(shù)據(jù)傳輸,這些數(shù)據(jù)傳輸代碼要全部做在main里單線程化,然后中斷函數(shù)里用flag標(biāo)志位來指引main里的數(shù)據(jù)傳輸代碼開始工作,這樣才穩(wěn)定!
96.
以前比如加速曲線有加速到加速曲線數(shù)組上標(biāo)就停止再加速的bug:改為【加速曲線數(shù)組走到后也不停止加速,而是加速到最大kHz1才開始勻速】:
if(x_cur_ruanqidong_ed==0){
if(x_cur_ruanqidong_c>=ruanqidong_num){
x_cur_ruanqidong_c=ruanqidong_num;
}else{
x_cur_ruanqidong_c=x_cur_ruanqidong_c+v70_jiasu_jiange;
}
if((x_cur_ruanqidong_c_khz>=kHz1)){//加速曲線數(shù)組走到后也不停止加速,而是加速到最大kHz1才開始勻速
x_cur_jiasuqu_max_val_reached=1; //v14
x_cur_ruanqidong_ed=1;//勻速區(qū)
}
}
97.
1個應(yīng)用軟件在操作系統(tǒng)里所分配到的cpu資源是有限的,所以應(yīng)用軟件里的線程里應(yīng)該盡量避免while+sleep死循環(huán)方式的等待某事件,這樣很耗cpu,導(dǎo)致軟件很卡頓或不流暢,盡量用while+多線程的信號量機制來解決,如 _autoResetEvent_UDPsend.WaitOne(5);//must 5,因為這樣萬一因為某種不穩(wěn)定而卡住也就只卡5ms
98.
數(shù)字開關(guān)電源板的高頻變壓器的次級需要比正常圈數(shù)多繞2圈,留點升壓空間,來防止變壓器變壓比達(dá)到最大而飽和而無法繼續(xù)隨占空比變大而升壓,進而導(dǎo)致的mos管在占空比飆升后燒毀!,另外,程序里設(shè)定的輸出穩(wěn)壓值要比正常額定輸出值低2-3伏,也是為了防止變壓器變壓比達(dá)到最大而飽和而
無法繼續(xù)穩(wěn)壓而導(dǎo)致的mos管經(jīng)常燒毀!
99.
float fabs_c(float f1){
if(f1>=0){
return f1;
}else{
return f1*(-1);
}
}
typedef struct PID
{
//int SetPoint; //設(shè)定目標(biāo) Desired Value
double Proportion; //比例常數(shù) Proportional Const
double Integral; //積分常數(shù) Integral Const
double Derivative; //微分常數(shù) Derivative Const
float LastError; //Error[ -1]
float LastDE; //Error[ -1]
float DE; //Error[ -1]
} PID;
float P_DATA=3;//3 這個要小,否則輸出電壓波紋大
float I_DATA=3;//3
float D_DATA=0.05; //0.05
//聲明 PID 實體
//*****************************************************
static PID sPID;
static PID *sptr = &sPID;
//*****************************************************
//PID 參數(shù)初始化
//*****************************************************
void IncPIDInit(void)
{
sptr ->DE=0;
sptr ->LastError = 0; //Error[-1]
sptr ->LastDE = 0; //Error[-2]
sptr ->Proportion = P_DATA; //比例常數(shù) Proportional Const
sptr ->Integral = I_DATA; //積分常數(shù) Integral Const
sptr ->Derivative = D_DATA; //微分常數(shù) Derivative Const
}
//*****************************************************
//增量式 PID 控制設(shè)計
//*****************************************************
float IncPIDCalc(float NextPoint,float SetPoint )
{
float iError, iIncpid; //當(dāng)前誤差
iError = SetPoint - NextPoint; //增量計算
sptr ->DE=iError-sptr ->LastError;
iIncpid = sptr ->Proportion * (sptr ->DE)
+ sptr->Integral * iError //E[k-1]項
+ sptr->Derivative * ((sptr ->DE) - (sptr->LastDE)); //E[k-2]項
//iIncpid = sptr ->Proportion*(iError)
//- sptr->Integral*(sptr ->DE)
//+ sptr->Derivative*((sptr ->DE) - (sptr->LastDE));
sptr ->LastDE = sptr ->DE; //存儲誤差,用于下次計算
sptr ->LastError = iError;
return(iIncpid); // 返回增量值
}
//*****************************************************
static PID sPID2;
static PID *sptr2 = &sPID2;
//*****************************************************
//PID 參數(shù)初始化
//*****************************************************
void IncPIDInit2(void)
{
sptr2 ->DE=0;
sptr2 ->LastError = 0; //Error[-1]
sptr2 ->LastDE = 0; //Error[-2]
sptr2 ->Proportion = P_DATA; //比例常數(shù) Proportional Const
sptr2->Integral = I_DATA; //積分常數(shù) Integral Const
sptr2 ->Derivative = D_DATA; //微分常數(shù) Derivative Const
}
//*****************************************************
//增量式 PID 控制設(shè)計
//*****************************************************
float IncPIDCalc2(float NextPoint,float SetPoint )
{
float iError, iIncpid; //當(dāng)前誤差
iError = SetPoint - NextPoint; //增量計算
sptr2->DE=iError-sptr2->LastError;
iIncpid = sptr2 ->Proportion * (sptr2->DE)
+ sptr2->Integral * iError //E[k-1]項
+ sptr2->Derivative * ((sptr2->DE) - (sptr2->LastDE)); //E[k-2]項
//iIncpid = sptr ->Proportion*(iError)
//- sptr->Integral*(sptr ->DE)
//+ sptr->Derivative*((sptr ->DE) - (sptr->LastDE));
sptr2->LastDE = sptr2->DE; //存儲誤差,用于下次計算
sptr2->LastError = iError;
return(iIncpid); // 返回增量值
}
==========
IncPIDInit();
IncPIDInit2();
=========
diffv1=IncPIDCalc(AD_Value1,Mubiao_val1);
diffv2=IncPIDCalc2(AD_Value2,Mubiao_val2);
ch_vout1_zhankongbi_value2=ch_vout1_zhankongbi_value2+diffv2;//由PID增量式控制算法返回增量并且賦給Voltage_2
if(ch_vout1_zhankongbi_value2<2){ch_vout1_zhankongbi_value2=2;}
if(ch_vout1_zhankongbi_value2>max_zhankongbi1){ch_vout1_zhankongbi_value2=max_zhankongbi1;}
TIM_SetCompare2(TIM1,(u16)ch_vout1_zhankongbi_value2);
==================================================================================
100.
異步非阻塞式按鈕軟件去抖算法+異步式等待按鈕釋放算法:
u8 btn_ccc;
u8 key_realsed;
u8 is_anykey_down;
u8 get_btn_key(){
is_anykey_down=0;
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0){
is_anykey_down=1;
if(key_realsed==1){
btn_ccc++;
if(btn_ccc>=10){
key_realsed=0;
btn_ccc=0;
return 1;
}
}
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)==0){
is_anykey_down=1;
if(key_realsed==1){
btn_ccc++;
if(btn_ccc>=10){
key_realsed=0;
btn_ccc=0;
return 2;
}
}
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)==0){
is_anykey_down=1;
if(key_realsed==1){
btn_ccc++;
if(btn_ccc>=10){
key_realsed=0;
btn_ccc=0;
return 3;
}
}
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)==0){
is_anykey_down=1;
if(key_realsed==1){
btn_ccc++;
if(btn_ccc>=10){
key_realsed=0;
btn_ccc=0;
return 4;
}
}
}
if(is_anykey_down==0){
if(key_realsed==0){
btn_ccc++;
if(btn_ccc>=20){
key_realsed=1;
}
}else{
btn_ccc=0;
}
}
return 0;
}
==================================================================================
101.
可靠傳輸中,整型int序列號式確認(rèn)流的穩(wěn)定性永遠(yuǎn)大于布爾變量式的確認(rèn)法(比如收到確認(rèn)包就置位)
==================================================================================
102.
u32 next_jishuo_stage1_a_zhou;
next_jishuo_stage1_a_zhou=(u32)(((float)1000000.00)/ ((float)a_zhou_mutli_soft_timer_hz));
和
u16 next_jishuo_stage1_a_zhou;
next_jishuo_stage1_a_zhou=(u16)(((float)1000000.00)/ ((float)a_zhou_mutli_soft_timer_hz));
的區(qū)別,
以及
(u16)(((float)1000000.00)/ (float)a_zhou_mutli_soft_timer_hz);
和
(u16)(((float)1000000.00)/ ((float)a_zhou_mutli_soft_timer_hz));
的區(qū)別,
以及
(byte)(x_diff_in_mcu1 >> 8 & 0xff);
和
(byte)((x_diff_in_mcu1 >> 8) & 0xff);
的區(qū)別,
容易造成bug
==================================================================================
103.
有時重新構(gòu)架(去掉各種不穩(wěn)定式機制后的新構(gòu)架)比在老架構(gòu)里找問題要來得快
==================================================================================
104.
單片機多定時器應(yīng)用代碼里,要注意可能不同定時器所用的主頻不同,比如stm32f407的APB1, APB2等上的不同定時器,別搞亂了
105.
服務(wù)器使用的雙滾珠風(fēng)扇,4040 的12V0.3A,接通電源之后噪音會比較大,但卻是不想使用4010的南北橋風(fēng)扇,不是滾珠的,有不轉(zhuǎn)的風(fēng)險。
106.
就單單才零點幾歐電阻的導(dǎo)線稍微長點來傳輸電源,比如20cm長,就會導(dǎo)致高頻雜波變多,所以導(dǎo)線末端務(wù)必加470uf以上的電解電容來去耦濾波,可以想象,在單片機引腳周圍1圈上也應(yīng)該最好加上幾個470uf電容和104電容環(huán)繞
107.
tc4427這個mos管驅(qū)動芯片的驅(qū)動輸出腳必須直連到mos管柵極,有電阻的話容易受串?dāng)_而周期性波動吱吱聲,比如串聯(lián)200R電阻就不行
108.
大功率pcb供電線走線千萬不要在單片機周圍走線或走過單片機,一定要把大功率電源的輸入接口做在大功率電路部分的附近,避免電源線的長距離走線!,要走也不要在單片機外周走過!,且大功率pcb供電線走線要邊走線邊1路隔個幾段距離就加個104去耦電容
109.
芯片打磨去字會產(chǎn)生靜電;它會損毀IC的,尤其是CMOS;壞的概率很高。
我們之前的MCU都是用銼刀磨,戴上靜電環(huán),壞的概率比較小,也可以先磨再燒,這樣只要能燒的通常是好的!
110.
.Net會自動完成垃圾回收,對于比較大的圖片,我們可以使用using關(guān)鍵字在不需要的時候自動對其進行回收
111.
多html標(biāo)記的夾死比單html標(biāo)記的內(nèi)包塊的穩(wěn)定性要好的原理,可以弄新安裝機制來防止html代碼格式不規(guī)范而導(dǎo)致的卸載時卸掉了不該卸的其他塊內(nèi)容:
img_elements2 = doc.DocumentNode.SelectNodes("//tr[@height='1."+yy.ToString()+""+weizhihao+"']");
if (img_elements2 != null )
{
img_elements2_end_flag= doc.DocumentNode.SelectNodes("//tr[@height='1."+yy.ToString()+""+weizhihao+"2']");
if (img_elements2_end_flag != null )//新安裝機制,可以避免[因html安裝體代碼不規(guī)范而導(dǎo)致卸載時時而卸少了,時而卸多了]的問題!
{
112.
C# winform里sql server連接串為string connectionStr = "server=服務(wù)器名;database=數(shù)據(jù)庫名;uid=用戶名;pwd=密碼";
,而c# asp.net里sql server連接串為"Data Source=(local);User ID=sa;Password=12;Initial Catalog=#XGM_SITE_DATATB_ICENTER#;Pooling=true;Max Pool Size=29800;Min Pool Size=50;Connection Lifetime=50" providerName="System.Data.SqlClient" />
,不一樣的,要注意區(qū)分,不然會出現(xiàn)連不上問題!
113.
double new_width = 0;
double new_height = 0;
Image m_src_image = Image.FromStream(ms);
if (m_src_image.Width > width_for_shoujiduan)
{ new_width = width_for_shoujiduan;
new_height = m_src_image.Height - (int)(((float)m_src_image.Width - (float)width_for_shoujiduan) * ((float)m_src_image.Height / (float)m_src_image.Width));
}
......
以上為經(jīng)典的不穩(wěn)定性bug的結(jié)構(gòu),如果 if (m_src_image.Width > width_for_shoujiduan)不成立則new_width=0,則引發(fā)接下來的bitmap的初始化異常,所以用于賦值用的if最好配else
114.
while ((m_WebBrowser.IsBusy || m_WebBrowser.ReadyState != WebBrowserReadyState.Complete) && aaa < 20 * 20)//不加m_WebBrowser.IsBusy 的話好像容易有最底下有1大片留白;最大等待20秒,10秒有點短
,然后網(wǎng)頁加載完時也等待IsBusy:
private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
WebBrowser m_WebBrowser = (WebBrowser)sender;
int aaa = 0;
while ((m_WebBrowser.IsBusy) && aaa < 20 * 20)//最大等待20秒,10秒有點短,因為有時候描述的高度比較長
{
Application.DoEvents();//給webbrowser用的
aaa++;
System.Threading.Thread.Sleep(50);//小點來讓Application.DoEvents()更多次,來輔助界面線程!
}
,另外用<div flag='ruanflagdiv1' style='width:750px; (即限定div容器和最小寬度:750px) :
rwfile11.write_a_file(System.AppDomain.CurrentDomain.BaseDirectory + "\\..\\maketemp\\" + file11a, "<html><body><style>body{max-width:790px;max-height:16000px;font-size:12px;padding:0px;margin-left: 0px;margin-top: 0px;margin-right: 0px;margin-bottom: 0px;} \r\nimg{border:0px}a{ text-decoration:none;}</style> <div flag='ruanflagdiv1' style='width:750px;margin:0px;padding:0px'> " + prod1_html1a.Replace("<body>", "").Replace("<Body>", "").Replace("<BODY>", "").Replace("</BODY>", "").Replace("</Body>", "").Replace("</body>", "") + " </div> </body></html>
|
|