久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2477|回復: 4
打印 上一主題 下一主題
收起左側

阮工的單片機編程經驗集:如何做穩定單片機程序與上位機程序防卡頓,js等經驗

[復制鏈接]
跳轉到指定樓層
樓主
ID:44609 發表于 2021-11-20 08:42 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
阮工的單片機編程經驗集:如何做穩定單片機程序與上位機程序防卡頓,js等經驗;阮丁遠于20211109 :
===================================================================

   小數轉整數時未先四舍五入而導致的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 = 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,防止下一個包影響上一個包的發包代碼后的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>
        /// 應用程序的主入口點。
        /// </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 = "出現應用程序未處理的異常:" + 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("應用程序線程錯誤:{0}", e);
            }
            errlog1(str + "系統錯誤002");
            Form1.refwin1.libcls.MessageBoxw("系統錯誤002:"+str);


        }
        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            string str = "";
            Exception error = e.ExceptionObject as Exception;
            string strDateInfo = "出現應用程序未處理的異常:" + 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 + "系統錯誤003");
            Form1.refwin1.libcls.MessageBoxw("系統錯誤003:" + str);
        }
    }
================================================================================================


                        USART_SendData(USART1, send_temp[ii]);
                    
                    
                        while(ccc1<1999999&&USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET){ccc1++;}
                    
                    
   會少一字節,而要:
                    
                        while(ccc1<1999999&&USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET){ccc1++;}
                    
                    
                        USART_SendData(USART1, send_temp[ii]);
                    
                    
第一字節消失的解決方案:調用USART_SendData函數之前,先將‘TC’標志位清‘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)
    {

}
}
?

================================================================================================

電壓輸出函數里加【int need_panduan_stop_jisozhuan_flag】參數,如果按了軟件里的急停按鈕,則先調用輸出0電壓的函數,然后所有jinrun_output_UI
里的if (need_panduan_stop_jisozhuan_flag == 1)的部分的調用全部直接return,來防止后續再意外輸出電壓
        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;

                }

            }
===================================================================
在調試stm8/stm32時,發現把單片機程序串口設置為8位數據位,偶校驗,上位機設置為8位數據位,偶校驗數據一直不對,當把單片機程序改為9位數據位,偶校驗,上位機設置為8位數據位,偶校驗數據就對了,不知道為什么?是不是st單片機的一個bug

================================================================================================
用 序列化類來保存參數時,各參數類務必不要含類的引用,而要用類的my_ID形式來保存引用哪個ID的類,否則序列化后類的引用丟失!!
================================================================================================
刪除元素時務必在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;

                        }

                    }
                }

================================================================================================
設計pcb時元件角度盡量一致,這樣制作貼片文件時簡單點,100個led燈以上時,led公共腳做成可選vcc或 gnd的,防止led貼反

================================================================================================
1.網絡等通訊時用 把整個 ini配置文件的字符串形式發出去,比用 | 號隔開每個項要好維護點!!

2.串口或網口通訊可以先用 解析協議里的長度字串方式或求crc是否通過的試探方式,比【sleep 60 ms加在串口接收中斷開頭】要快,
如果解析協議方式受不到包,可以再用【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指:需要立即 經過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已經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已經BeginRead

                                }
                            }
================================================================================================
參數類和狀態類等最好和工位一起統一為1個類,比如:
如果重構,就把
cp_info_cls_info類和pw_addr_used_info類都寫到OP_with_com_name類里!!!
,然后地址固定為 1+工位號,或yike協議的:20+工位號 , 而不要用分配地址方式,地址固定為和工位號有關是最穩定

================================================================================================
上位機要配mysql 綠色版,比 access穩定多了!!!

c#界面大字可以用textbox無邊框模式,
c#表格可以用 panel 的邊框模式,多個panel組裝成表格
================================================================================================
    多次重試初始化聲卡,防止初始化失敗:
   
    if (wm8978_Init()==0&&wm8978_Init()==0&&wm8978_Init()==0)//
    {
        
================================================================================================
        
多線程里每次加一的id之類做增量緩存機制,只第一次加并存到緩存,后續讀緩存,防止多加了一次!!
然后產品通訊下線時才清除此緩存
================================================================================================
以下可以防止點擊任務卡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異步讀取時的開頭里的妙用,解決數據包的半包和粘包問題,加大比特率解決延時慢,
ping多線程卡串口接收,
有的設備有保存標志,置位后才保存,
modbus3區寄存器不能用4區的命令來讀,
恒流源量程沒設對可能導致精度異常,

線程里多個函數調用,延時大而影響其他函數的實時性,應多線程化調用各個函數。
發送函數可以加lock機制,一次只發送一個,防止并發沖突
chkintertert開電腦時可能一直失敗而不穩定
歐姆龍plc監視模式下hostlink才能寫變量,考慮模式設置能長時間保存不
-----------------------------------------
對于要多處來回拷exe調試的情況,可以集中一臺電腦來編程,其他電腦只 mstsc遠程桌面式編程,編完后考慮exe到其他電腦,這樣可以防止源碼最新版本的版本混亂!!

,這樣也不好,可以用 seafile +服務端來同步源碼版本!!!,可以設置覆蓋時的歷史備份數!!
================================================================================================

單片機控制步進電機的代碼里步進脈沖由timer發生時,加減速曲線時timer的頻率的控制有  hz1=hz1+step的每步加上一個量的方式會很穩定而不丟步,
如果用hz1=hz_max*percent1的方式計算會丟步!!!,
另外,用當前左邊的絕對步數來輸出要運動的量比傳輸移動增量要穩定!!,可以消除浮點不精確性的累計誤差!!!
================================================================================================

20191207 :   
5V等電源輸入時不要串聯防反接用的二極管,否則由于二極管壓降大和內阻大,從而使電源續流能力和穩定性大大下降,
從而導致各種干擾時的不穩定性!
================================================================================================
74hc595等狀態鎖存芯片要定時main函數里刷新595芯片的狀態數據,就像DRAM一樣刷新,否則受干擾容易出錯位
================================================================================================
stm32+mos管驅動開關電源變壓器時,是否在mos管輸入極前接個變壓器做信號輸入耦合,否則如果stm32一死機則導致mos處于
一直導通的高電平狀態而燒毀,加了變壓器則stm32輸出死機時則無信號給變壓器次級而保護住mos管
================================================================================================
timer定時器里判斷io引腳輸入的狀態計數來獲得io狀態,比用main的loop循環里讀IO狀態要更實時,因為main可能執行其他代碼而對輸入錯過

================================================================================================

c#代碼需要try 包住,對于c#調用c++的dll時,所有函數得加[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程序里,注意:如果當前顯示的是子窗口且主窗口隱藏,則關閉窗口后會exe還在進程管理器里,
需要子窗口再closing事件里把主窗口的is_ruuning設置為0
================================================================================================
     SPI_FLASH_BufferRead(fft_model1, huanxingci_addr, NFREQ*Max_NFRAME*4);//
  
NFREQ*Max_NFRAME*4為100KB
,而最后一個參數類型為u16, 所以導致了讀取數據異常的奇怪bug

=========================================
有時keil打印浮點數時始終為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 數組存儲float變量時,
浮點(FLOAT)轉換為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轉換為浮點(FLOAT)
??char sBuf[4];
??sBuf[0]=0x33;
??sBuf[1]=0x33;
??sBuf[2]=0x53;
??sBuf[3]=0x40;
??float *w=(float*)(&sBuf);
???
---------------------

=========================================
c#里的thread函數里如有異步事件而無阻塞,則在執行初始化后在末尾加 while(true){ Sleep(10);},防止線程退出后相關變量等被注銷
=========================================
原來只是把formmark窗體 hide了,現在是close:

是否沒close導致formmark窗體多個實例存在而異常??
=========================================
穩定性=穩定X穩定X....,
所以如果網線通訊可靠,寧可用3個CPU+3個DM9000aep網卡+3個網口+1個路由器 形式來擴展,
也不要用 3個CPU+3個cpu間串口通訊或485通訊+1個DM9000aep網卡+1個網口 形式來擴展,
因為網口的可靠性已確認,多幾個也沒事,而串口通訊或485通訊穩定性未知,比如大波特率下的可能的異常

=========================================

   
    FMC_SDRAMInitStructure.FMC_SDClockPeriod改為FMC_SDClock_Period_3后播放網絡mp3時不死機了,說明要對sdram的線要手動布好,下次用等長線蛇形線!!!:

   
   
    FMC_SDRAMInitStructure.FMC_SDClockPeriod             = FMC_SDClock_Period_2;
    //FMC_SDRAMInitStructure.FMC_SDClockPeriod             = FMC_SDClock_Period_3;

  ,
  或者暫時補救的方法:SDRAM_GPIO_Config函數里把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;   
=========================================

改數組的長度或malloc的分配長度時,注意先搜索這個變量名,在文件中查找全部,看這個指針變量的最大寫入長度,防止數組下標溢出而死機,可以加超出長度的判斷和過濾
=========================================
01a1.程序做穩定的秘訣1:所有要申請內存的做到1個固定長度的數組里,用is_free來標記是否可用,而用malloc在多線程時申請內存太不穩定
01a2.電路里多個用電模塊可以分開用幾個穩壓芯片單獨供電,不要公用1個穩壓芯片
01a3.降低fsmc總線頻率或SDRAM頻率,或sdio總線頻率或i2c總線頻率則可能解決一些不穩定性故障

01a.異步方式的判斷某個io口的狀態時,可以用timer計時和記高電平數量等來做異步式的軟件式防抖算法!

0. 485工業總線通訊時,每次報文必須段,應IP+cmd方式,報文段則錯誤率小,
0b.w25q256 等存儲器,一切讀寫前先初始化時記得重置cs腳為高電平,不能不管cs腳狀態
,
    fatfs里 res=f_open(&ftemp,(const TCHAR*)main_script_outfname,FA_CREATE_NEW);沒有|FA_WRITE,則只能新建文件而無法寫入數據
            

0c.
stm32f407的flash寫入時,注意一個寫入范圍跨2個扇區時的問題,由于擦除前沒做緩存備份,所以寫入會導致老數據丟失,
可以用
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擦除期間,必須禁止數據fetch!!!搞了我兩晚上才發現這個問題!

   
   
   
   
    for (i = StartSector; i<=EndSector; i += 8)
  {
      while (FLASH_EraseSector(i, VoltageRange_3) != FLASH_COMPLETE)
      {
      }
  }
   
      FLASH->ACR|=1<<10;        //FLASH擦除結束,開啟數據fetch
   FLASH_Lock();
  
  
}
來提前擦除,然后統一用不帶擦除的寫入函數來分步寫入

0.數據層和邏輯層分離,比如讀寫操作可以單獨在1個線程里,外部需要讀寫數據庫得可以做個消息隊列傳入數據層里一個個執行 ,這樣就不會有數據庫鎖在高并發鎖死的問題和卡軟件的問題,還可以做讀寫分離,寫入數據時自動熱備份在多臺服務器的數據庫里,
0a.類似,相同的屬性和函數必須抽象為1個,集中在1個函數里,不要分布在每個母函數里,否則要改一個地方就得改所有調用的地方,牽一發而動全身

0b.可靠通訊里不要用if(packet_id!=cur_packet_id){......}等機制實現可靠性,而要用比如xy軸目標坐標步數是否等于上一次目標步數的,另外上位機處理返回包的id時要判斷recv_id!=last_send_id是否相等,不相等則丟包(包發送函數為單獨線程,并取消息隊列,一次只發一個,這樣穩定點)
機制實現可靠性,控制蜂鳴器的代碼也一樣:if(cur_to_beep_cc!=to_beep_cc){to_beep_cc=cur_to_beep_cc;....
而電磁閥等不需要滿足次數或步數等,可以直接丟包時不斷重發!!
1.中斷和 main函數或其子函數 里不要同時有同一外設模塊信號的操作,如對74hc595的操作,對HT1621B的操作的信號線必須是有序的,而不能穿插,否則容易錯亂或花屏
2.中斷里標志位變化時,回main函數或其子函數后盡量再判斷標記位,否則容易導致遺傳上次標志位的作用,而沒有及時發現中斷返回后標志位已改變,而導致程序混亂
3.類似于信號量臨界區的穩定性處理
4.對所有死等待如等待某標志位 ,做超時退出處理,防止卡死
5.所有數組下標盡量在訪問數組前都做下標是否越界判斷
5b.做除零異常判斷,如果分母為零就不除
5c.注意:如果程序里有很多個定時器或多個高速外部中斷在工作,而且定時頻率都很高,那么如果棧空間分配很小,那么很容易發生嵌套的多層次搶占中斷而在進入中斷函數時發生局部變量分配儲存空間時失敗即堆棧溢出,而導致死機和卡死!,盡量
把功能用分支if做到1個定時器中斷里,少用定時器,并盡可能減小定時器頻率,并盡可能加大棧初始化空間,如stm32單片機里 .s 文件里的一些定義
6.對于一些潛在的不穩定因素(甚至1-2個月才出現一次故障),比如網絡忙時包ID的先后次序發生變異,而導致if xxxx.id>old.id 永遠無法再成立而卡死
7.多機通信盡量用單向通信,比如主從模式,主機發包輪詢查詢并從機返回結果模式,雙向的全雙工通信模式的穩定性比較差
8.所有按鈕處理必須做按鈕去抖動算法,甚至不是阻塞式延時式的去抖動算法,而是狀態機時的異步流程方式的過濾掉按得過快的2次按鈕事件間隔,比如200ms內再無按鈕事件且按鈕已沒按住才算按了1次,而且最好加個104電容在按鈕里并聯
9.多機主從通信盡量用 目的機ID+通信協議版本號+分包的子包ID序號(如1-10)+子包總數+本子包數據長度+上次包的全局ID+crc32校驗,方式來處理重復包,30ms內沒收到從機反饋數據則重發包(比如重發4-6次)來處理丟包,并當上次包的全局ID大于一定值時發清零包ID的cmd包來復位ID計數,但要判斷從機先收到大點的ID的包的情況
10.網口通信比usb通信穩定,usb在干擾時容易掉線
11.TF卡只適合做只讀的fat32文件系統應用,如要常寫入,最好配合 W25q64來固定分片方式的無fat32文件系統式的寫入,因為TF卡常寫入,容易寫壞文件系統格式,比如意外掉電時等
12.對于一些串口接口的模塊,如果波特率用得太大,則單片機容易串口中斷響應不過來,而發生嵌套的異常串口中斷,或丟串口數據,導致通信不穩定,波特率最好低點!
13.單片機程序的架構最好做成異步式的狀態機式的虛擬多線程方式,不要用任何阻塞式的死等待和延時,延時也要做成狀態機里的某個狀態的delay的異步非阻塞式的累加,加到一定值才進入下1個狀態碼,異步方式比阻塞式的方式的穩定性要好,處理實時性也高,多級菜單的屏幕顯示和按鍵處理也可以用狀態機式的異步流程,有個狀態碼指示當前菜單的第1級選擇項號,選擇深度,第2級選擇項號。。。。
14.相同的類似的屬于同一功能概念下的處理過程最好集成到一個函數里做分支判斷,而不要把1個功能分散到很多個函數里,這樣代碼比較難維護和二次修改
15.可以抽象為1個功能或1個流程體內的東西最好歸納法和抽象法抽象一下,不要做成if分支遍歷和窮舉所有情況方式,就像要做成矢量圖,而不是像素圖,抽象概括后的代碼寫法更有簡潔性,更
有可維護性和穩定性!
15b.復雜度盡量做成可配置數組里的數據,用配置數據來展開對代碼的控制,而不是把復雜度全部映射到代碼if分支等分支上,就像要做成矢量圖,而不是像素圖,就像一個圖形圓在可配置數組里(矢量圖里)是 yuan_type和sin cos,而若是代碼分支法,則是一堆關于圓的點陣
15c.按15點所說的抽象和歸納代碼后的代碼簡潔化和可維護性加大化,可以使代碼的條理和邏輯層次更加清晰,使更容易分析和預感到可能的bug和細節邏輯不嚴謹,從而預防bug和不穩定性bug,最終影響程序運行時穩定性,所以bug是由思維決定的

16.看門狗在程序完工后最好加上去,軟件里的關鍵數據最好做RAM區多處備份,如備份10份,死機自動復位恢復后,檢查RAM區里備份里的各備份,重復次數最高的那幾份備份(且重復次數在6次以上)最可能為原始未被破壞的數據,并做個ram標志位檢查是上電復位,還是看門狗復位,復位后可以做不清除ram數據,程序跑飛后,可以提前做些標志位判斷,如跑飛到某處,可能某標志位未被置位卻執行到這,則可以自動判斷出是已跑飛,則自動軟件復位一次!
17.
單片機芯片的最近的外圍一圈上的單片機芯片vdd,gnd引腳附近必須多弄幾個0603封裝的貼片104電容來去干擾,pcb電源類走線的線寬必須要寬,至少0.4mm以上,甚至0.8mm以上,單片機芯片在pcb局部時盡量靠近電源芯片和供電芯片處

就單單才零點幾歐電阻的導線稍微長點來傳輸電源,比如20cm長,就會導致高頻雜波變多,所以導線末端務必加470uf以上的電解電容來去耦濾波,可以想象,在單片機引腳周圍1圈上也應該最好加上幾個470uf電容和104電容環繞
;大功率pcb供電線走線千萬不要在單片機周圍走線或走過單片機,一定要把大功率電源的輸入接口做在大功率電路部分的附近,避免電源線的長距離走線!,要走也不要在單片機外周走過!,且大功率pcb供電線走線要邊走線邊1路隔個幾段距離就加個104去耦電容
18.
如果是軟件模擬spi信號接口或i2c接口,那么字節發送函數里最好先禁止總中斷使能,發完后再開啟總中斷使能,否則容易不穩定
19.
c8051f340單片機芯片工作時如果電源電壓比較低或有突然的電源脈沖浪涌干擾,容易使燒好的程序丟掉,穩定性好像不怎么好,stm32單片機芯片或GD32芯片好像就好點,就沒這個問題!,推薦優先選后者
20.
對于以太網網絡模塊芯片應用,最好把這芯片的RST復位信號接到單片機上,并RST腳接個104電容(有時候干擾容易導致意外硬式復位,而反而不穩定),這樣,網絡卡死時可以強制復位網卡芯片一次來修復
21.
lwip等網絡協議棧的某些版本有可能有內存泄露bug,或長時間運行后卡死,最好做定時自動軟件復位單片機功能,來防止內存泄露過度
22.
芯片上如果flash夠,最好做從服務器查詢新版BIN固件來自動升級單片機自身的hex固件功能(固件下載完要做CRC32校對),以及usb的固件升級功能,即net bootloader和usb bootloader,來應對【做項目即使完成和驗收后,幾個月出現的bug要修復,而措手不及】

23.
比如 休眠后喚醒時給PB1引腳1個高電平后不明原因的死機,不一定是進入未定義中斷,而是main函數之前bootloader對PB1響應:主程序進入休眠態,喚醒后 pb1為高,進入bootloader態,但bootloader里沒有重新初始化stm32 cpu的時鐘系統等,故卡死,雖然bootloader后跳轉到的main里有重新初始化stm32 cpu的時鐘系統等的代碼,但是已執行不到

24.
密腳芯片單片機等ic焊接時上松香吸錫后,如果用酒精清洗,是否過幾個月后會發生引腳間松香,助焊劑等物質發生變質而短路?而不穩定?
25.
故障實時監測功能很重要!(備災1):比如某stm32版的帶網口的板子做個板子是否在線監測和掉線時短信提醒功能,即每10秒鐘發送1個心跳包給服務器,如果服務器50秒內沒收到某板子的任何心跳包,則此板子判為已掉線,并發送報警短信,email給對應的技術員來通知處理和修復
26.
同一個安裝點上弄多個冗余板子,某塊故障時,另1塊自動切換到上線!,或雙電源,1個后備電池逆變電源,主電故障則自動切換到備電(備災2)
27.
電源接插頭不要用圓孔的那種電源插座或usb接口插座,要用專用的接線端子母座和接線端子插頭(綠色方形的帶2針的那種,最好還帶上扣機制,插緊后自動上扣),否則電源不穩或接觸不良,導致板子掉程序或其他異常,信號線也可以如此處理

28.
回流焊最后1段溫度曲線的達到最高溫度時如保持時間過長(推薦205度保持15秒,對于無鉛錫膏)導致錫膏被烤干,或錫膏過期了,就會導致出來后焊接效果不好,比如焊盤上錫的亮度不高,無光澤,太干燥,這時容易影響產品性能和加速日后的產品性能衰變
29.
eeprom寫入前不需要擦除整個扇區,比如at24c02之類的,而單片機自帶的flash和w25q64等都是flash,寫入前如果不擦除整個扇區,可能部分字節會寫錯字節,而導致異常和不穩,或者做了擦除處理,但是寫某個數據時
未備份扇區里原數據而直接擦除扇區,導致數據丟失,比如要寫入的數據數組跨越了2個扇區的邊界的情況
30.
盡量不要用一些外置的什么is62lv256+ls373方案的擴展ram內存方式,而要直接用自帶高ram大小的192K ram的單片機,比如stm32f407或stm32f103zet,stm32103re ,gd32f103cb等,因為外擴方式容易不穩定,比如受焊接質量,打板精度,環境濕度溫度,電源波動等影響
而不穩

31.
遞歸函數盡量用while/for循環+棧或數組或鏈表的方式來替代,而不要做成多層次調用函數自身方式的遞歸,做也做成帶遞歸深度變量的,限制最大遞歸深度,否則容易堆棧溢出而死機
32.
當要進行可能的后果極其危險或非常重要,不能被任何干擾誤觸發的控制操作時(比如房屋爆破隊驅動炸藥點火的裝置,自動駕駛的緊急剎車制動驅動器,高壓總閘開關繼電器),務必要備份10份以上的
控制碼,如果只有1份控制碼,當這份控制碼被強干擾信號改變內存數據而變為導致觸發的目標控制碼時,就會誤動作,而如果是10份,會校驗所有10份控制完全一致后再打開控制,這樣只有1份或2份被干擾
后就不會誤動作了,所有內存區的任何變量也都實時備份10份以上,這樣被干擾后可實時恢復正確的數據(殘留下的重復次數最多的那幾份且重復次數在6次以上即為未破壞數據),
當10份備份中重復次數在6次以下,意外著系統也無法自己自我修復,就會自動進入崩潰狀態或自動軟式復位,
再進行重要控制或驅動前,也檢查所有內存區的各變量的實時備份的10份數據,如果任何一個變量的10份備份中各數據間重復次數在6次以下,則意外著干擾太強了,系統已錯亂,則不繼續進行重要控制或進行驅動。
,進行i/o控制的代碼最好在前面多加幾百行陷井代碼和判斷是否已被干擾弄崩潰的代碼,來盡可能減少意外直接跑飛到【進行i/o控制的代碼】的概率,或者不用任何簡單的i/o來控制重要設備或高壓的重要設備,
而用i2c或spi接口的外圍驅動芯片來間接控制,且i2c或spi必須為軟件模擬,在i2c或spi的發字節函數里,每發1個比特就再判斷一次【是否已被干擾弄崩潰即檢查所有變量的10份備份是否還算完整和一致】,
這樣就非常可靠和穩定,不會被強干擾弄誤觸發了!

33.
在存在強電流,強功率,強電壓,強靜電,強射頻耦合傳遞,或可能有接觸式火花的地方盡量多的加光耦隔離來隔離信號控制計算處理部分和高壓信號驅動部分,如果一個系統上存在2個或以上獨立供電電源,
相互之間也必須完全隔離(GND等都不要接一起),用光耦耦合來傳遞控制,
繼電器盡量換為可關閉的晶閘管,或場效應管,或其他大功率開關管,這些控制開斷時有【無火花】的優點,防止繼電器通斷時的火花放電間隙導致的強干擾,如果實在要用繼電器,在繼電器的2觸點引出腳間接入RC去火花電路,
,并在繼電器的2觸點引出腳控制的真空電磁閥或電機等感應負載上反向并聯入【1個高耐壓二極管+串1個0.5歐左右的大功率電阻】,以及并聯入1個體積大的104電容,來去除真空電磁閥或電機等在斷電瞬間產生的
反向電動勢,從而消除反向電動勢在繼電器觸點剛斷開時的微小間隙里產生火花,從而消除火花產生的強干擾來使單片機跑飛,死機或復位的后果,

,另外如果用繼電器,最好每個繼電器的VCC串1個10歐的電阻后再控制繼電器,這樣繼電器吸合時對電源的脈沖干擾小點!

34.
做編程項目或電子項目,務必做一個 發現的未解決的程序bug的備忘.txt ,及時記錄發現的偶爾出現的bug,和突然靈感里閃現的預感到的可能會出現的bug情況,以及要改進的地方,
比如一些操作不變雞肋問題,穩定性問題,都要及時記錄,因為如果每忘記1點點bug,都意味著可能日后會出現這個bug,即導致項目的徹底失敗,畢竟【一個項目的成功是靠堵住所有一些可能的細節蟲洞和雞肋來做到的】
35.
如果是在別人的開放平臺或接口上做程序,那么需要定期檢查所有情況都是否正常,比如即使今天全部正常,但是他們平臺也許早就換了接口版本,只是老用戶還是維持老版本接口,新訂購的用戶用我們isv軟件時卻已切換到新接口,不能用,卻發現不了
36.
字符串數組即二維的uchar數組,在定義時,前1個方括號索引字符串序號,后1個方括號索引當前字符串的字符下標,比如u8 wavfile1n[7][15] 代表7個最長14個字符的字符串,方括號別弄反了,容易異常

37.
板子上電源處加的電解電容在1000uf以上時,容易上電時電容瞬間電流過大而導致開關電源發生短路保護,如此循環,而不易啟動

38.
EMI:pcb電源輸入部分增加共軛電感器抑制中高頻的共模噪聲,并加共模濾波電容(2個電容串聯,2端接電源正負,中間點接地)和差模濾波電容
39.
如果要抗雷擊浪涌,應在電源輸入處并聯入動作電壓大于額定電壓2-3倍以上的壓敏電阻,并且電源輸入處pcb走線寬度要粗,并電源輸入處的壓敏電阻前的走線串聯入玻璃保險管的座的相關元件
40.
pcb信號線的走線盡量短而且寬: pcb布線時選擇合理的導線寬度 由于瞬變電流在印制線條上所產生的沖擊干擾主要是由印制導線的電感成分造成的,因此應盡量減小印制導線的電感量。印制導線的電感量與其長度成正比,與其寬度成反比
晶振走線必須盡量短:           時鐘線、信號線也盡可能靠近地線,并且走線不要過長,以減小回路的環面積
信號線走線拐角應采用圓弧形:   電路板上的印制線寬度不要突變,拐角應采用圓弧形,不要直角或尖角。(9)時鐘線、信號線也盡可能靠近地線,并且走線不要過長,以減小回路的環面積。
保持環路面積最小,降低干擾對系統的影響,提高系統的抗干擾性能。并聯的導線緊緊放在一起,使用一條粗導線進行連接,信號線緊挨地平面布線可以降低干擾。電源與地之間增加高頻濾波電容
采用完整的地平面設計,采用多層板設計,鋪設地層,便于干擾信號泄放。
41.
密腳芯片懷疑存在連焊時的處理技巧1:拖焊時務必放很多松香到吸錫銅絲線上,否則容易吸錫不徹底,而留下ic引腳間不穩定性的短路問題,最后用99%濃度(不能含水多)的酒精擦去殘留的松香,并再烙鐵順引腳刮下來清理一遍酒精等

42.
盡量用全局變量或全局數組變量替代局部變量,因為進入函數時要不斷的分配局部變量,容易影響執行效率和耗用完堆棧空間等;
43.
電源部分的正常和穩定(比如電壓波紋小,高頻噪聲濾除率高,功率實足,抗突變的浪涌大電流能力強)是單片機電路和控制電路穩定工作的最重要的前
44.
像LM1086-3.3或LT1086-3.3,LMXXXXX....之類的貼片式三端穩壓器,在貼片工藝完后并過回流焊爐后,由于焊盤上印刷錫膏時錫膏量不足或者三端穩壓器引腳上翹會導致三端穩壓器過回流焊爐焊接后存在虛焊或接觸不良,
所以應該每次都手工補焊下來補點錫,否則容易不穩定,或在電子產品運輸工程中脫焊而開路
sot23封裝的貼片三級管也容易引腳上翹而導致回流焊的焊接質量不良而導致開路或虛焊(如在運輸過程中振動而脫焊)
45.
所以出廠前的24h老化測試和pcb帶電工作的高強度振動測試也很重要,來檢測是否有虛焊的現象(如在運輸過程中振動而脫焊)
46.
電源開關等不要用機械式的單刀雙擲式的那種微型撥動開關,很容易損壞或接觸不良,最好做成自鎖式的那種按鈕式電源開關或船型開關

47.
注意場效應管的前置驅動電壓放大級用的三極管的發射極最好是接地形式的而場效應管G端接三極管的集電極再1個上拉電阻上拉到比如24V,否則如果NPN的發射極接G端時,當三極管導通,
發射極的電壓最大也只有基極的電壓的大小,將無法良好的使場效應管完全導通,比如IRF540N的Vgs需要10V左右才完全導通,另外應該在G端對地并聯1個10V穩壓管,因為一般Vgs也不能太大,20V左右最大了
不要用單片機IO口直接驅動場效應管,因為一般場效應管Vgs在10V左右才能完全導通和飽和,單片機io口電壓不夠,會驅動不良
48.
光耦存在壽命,所以盡量用非光耦的取代元件來做隔離,比如一些ic:
光耦中的發光二極管隨著時間推移而老化;比如開關電源反饋環路中光耦老化,傳輸比下降后可能引發故障。
不能用線性光耦作為模擬量傳感:通光耦產品手冊中對電流傳輸比只給出一個大概的范圍(從低到高可以差別一倍),而且沒有給出溫漂和老化的指標。用于開環傳輸模擬量,精度是得不到保證的,所以導致過壓、過流,以致損壞
49.
74hc595的時鐘和數據線上要加阻容濾波的  這類IC  速度太高   對毛刺干擾特別敏感,而導致誤動作或屏幕錯亂
49a.
做電壓檢測電路時,比如測220V電壓或380V電壓,不要用線性光耦+1M電阻來降壓采樣,而要用微型工頻變壓器(鐵芯那種(非高頻磁芯))來降壓并整流采樣,因為光耦會老化和被擊穿(雷擊等意外),或有溫飄
50.
不要在任何中斷函數里調用1個需要等待另1種中斷函數來置位目標所需標志位才能結束的函數或代碼段,否則可能因為中斷優先級的關系而導致這另1種中斷函數永遠無法執行而卡死在等待里,導致死機
51.
謹慎使用keil自帶的庫函數之sprintf和printf,容易出問題,比如卡死或內存泄露
52.
LQFP64/LQFP48之類的密腳IC如果用拖焊法手動焊接,那么如果芯片在pcb上的每個引腳的焊盤的長度如果比較長,則很容易拖焊后在引腳的焊盤的靠芯片內部方向處產生錫的殘留而導致2個引腳焊盤間的短路,所以
此法不穩定,還是用鋼網刷錫膏+回流焊法比較可靠!

53.
ad采樣均值濾波法時應該用:
        
            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)個字節,而不是(junzhi_lvbo_count-1)個

54.
數字電源或開關電源的mos管的驅動級的信號輸入最好帶一級最大高電平時間限制的硬件式電路,比如最大允許高電平500us,來防止卡死時一直高電平而使mos一直導通而燒壞mos或高頻變壓器!

55.
注意臨界大小判斷時容易出現的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,那么就會陷入死循環,永遠無法達到max_zhankongbi1,而且不是只離max_zhankongbi1一點,容易使調節環在誤差比較大時卡死
,所以要加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.
數字電源的每次反饋調整pwn或每次pid調整時的調整間隔周期必須在相隔500us左右以上而不是1個while死循環里不停的調整,
而要用timer定時定間隔的調(所以輸出級的電解電容務必2200uF以上,來延長電壓突變緩沖時間),而且每次調整的誤差比例乘積因子必須要小,否則容易輸出電壓波紋很大!
控制輸出電壓波紋的關鍵是這500us內不斷的采樣adc(可多次DMA式adc)來累加并在500us結束時取累加值的平均值來數字濾波,因為一般ad采樣電壓的波紋比較大

57.
keil中 3.11/6.1會被當做小數來計算結果,如果是3/23,則結果為0,因為被當做整數來處理,所以必須寫為(float)3.0/(float)23.0
58.
如果需要動態改變單片機定時器的定時周期,那么要注意周期定時值不能被改的太小,否則會導致不斷的高速的進入定時中斷而卡死,要判斷和限制下

59.
【bug調試和發現bug原因,解決問題現象】的原則是【在相對系中尋找局部絕對系來作為支撐點,進而慢慢全部覆蓋問題機制】,比如找幾個確定點和可以差量法,對比法確定的程序現象和機理,然后再慢慢消元,減少變化量的個數,盡量避免多個量
一起在變的問題框架,因為變量1多,要測試的樣本就成指數級增長!

60.
c#上位機程序防卡頓經驗1:
阻塞式的任務和代碼要全部在后臺線程里獨立運行,而不能在主界面的主線程里執行,對于后臺線程里要跨線程操作主界面控件的,這段操作界面的代碼要用到Invoke,相當于卡死界面一點時間,所以在
Invoke里調用的 Action<string> actionDelegate3 = (x) =>{。。。。}之類的里的代碼盡量執行時間耗時極短,比如盡量把界面控件某狀態的死等待代碼不要放在Invoke的執行體代碼里,
比如 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }這段等待webbrowers加載網頁完畢的代碼不要放在Invoke的執行體代碼里,而放在
線程代碼里,即Invoke的執行體代碼執行完后的緊接著的下一句代碼再執行 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }
,而Invoke的執行體代碼里如果執行webbrowers1.Navigate(aaa1.url);之類的,也會在打開網頁時卡頓一段時間界面,所以盡量改為異步方式的Navigate函數,比如BeginNavigate()之類的
61.
C#里等待webbrowser是否加載網頁完成的穩定可靠的檢測方法為:延時式再判斷累計法(注意:線程里讀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秒內都無Busy狀態再出現,那么極有可能網頁和其所有網頁子框架都已加載完成
                          {
                              break;
                          }

                      }

62.
c#里容易異常的地方和一些界面控制代碼外圍盡量包1層try ...catch,來防止崩潰出錯
63.
c#里object 不能直接隱式轉為int,而要先tostring()后再int.parse?

64.
js里的ajax里提交后盡量加&r="+Math.random()后綴來防止緩沖而不更新
65.
路徑可能變動的網頁里的帶背景img定義的css盡量不要放在網頁里,而放在一個外部css文件里,這樣圖片可以相對css文件來定義路徑
66.
對于ajax點擊鏈接執行函數但不跳轉,建議不要用href:void(0)...+onclick(兼容性不好,有些瀏覽器異常打開新窗口),而用div+onclick+div加手型鼠標css
,而且onclick所在div要顯式定義好height和width,否則容易兼容性不好,而有的瀏覽器點擊無響應!
Example:CSS鼠標手型效果 <a href="#" style="cursor:hand">CSS鼠標手型效果</a>
Example:CSS鼠標手型效果 <a href="#" style="cursor:pointer">CSS鼠標手型效果</a>
注:pointer也是小手鼠標,建議大家用pointer,因為它可以兼容多種瀏覽器。

67.
js里或帝國cms里esetcookie('last_xianhua'.$pinlun_id,$time1,time()+3600,0);的設置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發送1次模擬按鍵是為了觸發input輸入框的onchange,而最終還是強制用.SetAttribute("value", arr1[uidto_index].Trim());來正確的設置值,防止SendKeys.SendWait發送時丟字符(比如界面很卡時或手動轉移輸入焦點就容易丟)
,即SendKeys.SendWait+ .SetAttribute("value", 結合法
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.
各項都改好和文件都復制好了,還是故障依舊,那么很可能問題不在文件本身,而在文件的訪問權限給對應的服務進程沒賦予權限,比如:php怎么也加不入mysql組件,php.ini和庫,dll擴展都復制了也一樣,此時很可能是system32和外面的php.ini,庫等的安全屬性沒賦予iis賬戶和network等

71.
穩定性還含過熱保護穩定性:如果pcb設計上含帶過熱保護的大電流芯片,比如THB7128或者一些開關電源的過熱保護,所以出廠前要設置最合適的THB7128工作電流來使連續工作1周或更長都不會發生過熱保護而罷工
72.
對于大功率的處理器或單片機,比如DSP,ARM9-11等,發熱量很大的,那么必須加風扇散熱,否則cpu的高溫度會導致運行速度下降和更容易程序跑飛,從而導致死機和崩潰!:比如一些路由器的死機很可能是散熱不好,沒加風扇,或電源部分用的老式的變壓器式
的低輸出電流且容易受電網電壓欠壓影響輸出電壓的電源,而不是強悍低波紋且穩壓范圍廣的開關電源
優化散熱+自動重啟自身(用于解決可能的內存泄露等)可徹底穩定路由器:
大多數的路由器散熱都做得不是太好,尤其是CPU;
我的一個N年前的NW618 經過改造后,連續無障礙運行了4、5年了;
首先是主要發熱部件的散熱,車間里找銅皮、銅板——最好是紫銅,然后做好防短路處理,手工折好形狀——也是個稍微考驗腦子的活,最好是高度剛好在扣上蓋后能牢牢壓住;
NW618可以刷固件,刷了個番茄的;
設置定時自動重啟;
別管多熱,這個路由器都沒給我填過麻煩;
路由器、貓 絕大多數的散熱都很差,所以,提醒各位注意降溫;
我是拆了3個以上的無線路由器后才發現的…… ;
73.
div+css模板里div塊間的分界html注釋是div里的table化,有利于節約閱讀情緒成本

74.
伺服電機速度響應:加速區才幾ms而一般步進要100-200ms+最好用柔性聯軸器(因為加速區時扭矩很大)

75.
伺服電機套裝的伺服電機簡易買無刷的3線接口的直流無刷電機 ,有刷的電機不耐用
76.
數碼管熄滅時再做ADC,比如4位數碼管顯示,分5個時間隙,假如2ms一位,則前面8ms顯示,然后關閉顯示2ms后做ADC,避免LED電流對ADC的影響。外掛的ADC是因為LED的電影響不到它。
ADC部分沒有獨立的模擬地和電源的MCU,就要這樣考慮。
77.
實踐證明:smt中,貼片式TF卡座過回流焊后很容易虛焊(焊腳上翹現象),所以每次必須手工補焊一次
78.
實踐證明:部分旋擰方式的可調電阻內部有些是開路的,壞的,必須出廠測試
79.
不用【多層if或[多個單層if+return]】實現逐層條件判斷的方法: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任務切換調用中斷的優先級必須最低,防止實時任務嵌套中斷被任務切換中斷所打斷而異常!!!
82.
freertos里的
#ifndef traceMALLOC
    #define traceMALLOC( pvAddress, uiSize )
#endif
#ifndef traceFREE
    #define traceFREE( pvAddress, uiSize )
#endif
可以用于內存泄露的內存分配和回收的對稱性檢測,lwip里應該也有類似的機制
83.
freertos里采用heap_4.c這個內存管理方案來編譯進:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 20 * 1024 ) ) //采用heap_4.c才有此選項,即不交給keil的malloc函數來分配內存,而由系統管理

84.
如果數據傳輸要不斷對新連接分配臨時內存區,那么不定長的分配的和回收而留下內存空間間隙的話,久而久之會導致太多的內存碎片,是否最后導致性能越來越慢,甚至內存分配失敗而卡死?,如lwip?,所以單片機內存分配簡易采用數組劃分定長分配內存區的策略才穩定!
,配合configMINIMAL_STACK_SIZE設置夠大來使定長分配內存,而減少內存碎片積累
85.
Heap_Size       EQU     0x00010000  ;64k作為堆大小,即用于freertos的malloc分配內存,來使http_connect_task連接不會內存分配失敗,配合configMINIMAL_STACK_SIZE設置夠大來使定長分配內存,而減少內存碎片積累
86.
總線頻率要留有余量,比正常能驅動外設的最高頻率還稍微低點,才能穩定:
    FSMC_NORSRAMTimingInitStructure.FSMC_CLKDivision =12;//2分頻的話驅動dm9000a不起來,總線頻率太快!!,降為12分頻就可以

87.
如果非要用usb轉串口方案,穩定性和兼容性比較:FT232>PL2303雙芯片>ch340,如何查某種usb轉串口線所采用的芯片型號?:設備管理器里查設備屬性的VID和PID法
   
88.
有些以太網網絡芯片要做斷線檢測并自動重連,否則很容易斷線后徹底卡死無法收發數據,比如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.
注意單句編程語句寫法和次序不同而導致的穩定性不同,如:
     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#等對于一些多線程用的組件,在線程主入口函數里的末尾務必加while(true){sleep(10);}之類的死循環來卡住,否則 線程主入口函數里新建立的子線程會因為線程主入口函數返回了而沒卡住,而在不久后會全部注銷子線程和變量,而導致一些運行一段時間后莫名其妙的exe退出現象!
91.
if((args.data[1]&128)==128)與 if(args.data[1]&128==128)可能有區別,因為可能==優先于邏輯與&,所以  if(args.data[1]&128==128)可能會變為  if(args.data[1]&(128==128)),即 if(args.data[1]&1)
92.
按照我做開關電源的經驗,八成是變壓器出問題了,一個開關電源里面的所有器件的參數都比較可控,比如耐壓、耐流、功率、容值、阻值等等,唯一難以把控的來料就是變壓器,每批的參數都會有偏差,找找沒炸雞的電源,測試一下MOS上面的尖峰電壓吧,最有可能就是變壓器漏感超標導致尖峰電壓過高擊穿MOS,造成連串的燒毀!:換更高耐壓的mos管才是根本解決辦法,比如IRF540N換為IRF640N
         
93.
程序防破解的原理:【可利用的特征的消滅技術,加大破解時間成本】:運用【變中有變】原理:在原程序正常功能的變量變化鏈之中再套入1層變化,而這層變化是防破解用的,破解者無法識別是正常變化還是防破解用的變化,除非理解了我的程序,但是如果有理解能力,自己都能寫代碼,對這個矛盾加以利用即可:
,如正常代碼功能里對uuu[1]的值進行判斷和分支,但是我們如果識別到軟件未注冊,那么就可以在判斷未注冊后,不做簡單的返回,而是對uuu[1]的值取些非正常分支判斷需要的值
94.
數字電源板開關頻率可以設到36khz,不要15khz,15khz容易在負載功率波動時輸出電壓不穩!,而64khz又容易使輸出級整流管過熱!
95.
不要在中斷函數里做網口發包 ,usb發包,串口發包等數據傳輸,這些數據傳輸代碼要全部做在main里單線程化,然后中斷函數里用flag標志位來指引main里的數據傳輸代碼開始工作,這樣才穩定!
96.
以前比如加速曲線有加速到加速曲線數組上標就停止再加速的bug:改為【加速曲線數組走到后也不停止加速,而是加速到最大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)){//加速曲線數組走到后也不停止加速,而是加速到最大kHz1才開始勻速
                                             
                                x_cur_jiasuqu_max_val_reached=1;  //v14
   
                              x_cur_ruanqidong_ed=1;//勻速區
                             }
                           
                           
                           
         }

                              
97.
1個應用軟件在操作系統里所分配到的cpu資源是有限的,所以應用軟件里的線程里應該盡量避免while+sleep死循環方式的等待某事件,這樣很耗cpu,導致軟件很卡頓或不流暢,盡量用while+多線程的信號量機制來解決,如  _autoResetEvent_UDPsend.WaitOne(5);//must 5,因為這樣萬一因為某種不穩定而卡住也就只卡5ms
98.
數字開關電源板的高頻變壓器的次級需要比正常圈數多繞2圈,留點升壓空間,來防止變壓器變壓比達到最大而飽和而無法繼續隨占空比變大而升壓,進而導致的mos管在占空比飆升后燒毀!,另外,程序里設定的輸出穩壓值要比正常額定輸出值低2-3伏,也是為了防止變壓器變壓比達到最大而飽和而
無法繼續穩壓而導致的mos管經常燒毀!

99.
float fabs_c(float f1){
   
   
    if(f1>=0){
        
        return f1;
        
    }else{
        
        
        return f1*(-1);
        
    }
   
   
   
   
   
   
}

typedef struct PID
{
//int SetPoint; //設定目標  Desired Value
double Proportion; //比例常數  Proportional Const
double Integral; //積分常數  Integral Const
double Derivative; //微分常數  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 參數初始化
//*****************************************************
void IncPIDInit(void)
{
sptr ->DE=0;
sptr ->LastError = 0; //Error[-1]
sptr ->LastDE = 0; //Error[-2]
sptr ->Proportion = P_DATA; //比例常數  Proportional Const
sptr ->Integral = I_DATA; //積分常數 Integral Const
sptr ->Derivative = D_DATA; //微分常數  Derivative Const
}
//*****************************************************
//增量式 PID 控制設計
//*****************************************************
float IncPIDCalc(float NextPoint,float SetPoint )
{
    float iError, iIncpid; //當前誤差
    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 參數初始化
//*****************************************************
void IncPIDInit2(void)
{
sptr2 ->DE=0;
sptr2 ->LastError = 0; //Error[-1]
sptr2 ->LastDE = 0; //Error[-2]
sptr2 ->Proportion = P_DATA; //比例常數  Proportional Const
sptr2->Integral = I_DATA; //積分常數 Integral Const
sptr2 ->Derivative = D_DATA; //微分常數  Derivative Const
}
//*****************************************************
//增量式 PID 控制設計
//*****************************************************
float IncPIDCalc2(float NextPoint,float SetPoint )
{
    float iError, iIncpid; //當前誤差
    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序列號式確認流的穩定性永遠大于布爾變量式的確認法(比如收到確認包就置位)
==================================================================================
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));
的區別,
                                 
以及

(u16)(((float)1000000.00)/ (float)a_zhou_mutli_soft_timer_hz);

(u16)(((float)1000000.00)/ ((float)a_zhou_mutli_soft_timer_hz));
的區別,
以及
(byte)(x_diff_in_mcu1 >> 8 & 0xff);

(byte)((x_diff_in_mcu1 >> 8) & 0xff);
的區別,
容易造成bug

==================================================================================

103.
有時重新構架(去掉各種不穩定式機制后的新構架)比在老架構里找問題要來得快
==================================================================================
104.
單片機多定時器應用代碼里,要注意可能不同定時器所用的主頻不同,比如stm32f407的APB1, APB2等上的不同定時器,別搞亂了

105.
服務器使用的雙滾珠風扇,4040 的12V0.3A,接通電源之后噪音會比較大,但卻是不想使用4010的南北橋風扇,不是滾珠的,有不轉的風險。

106.
就單單才零點幾歐電阻的導線稍微長點來傳輸電源,比如20cm長,就會導致高頻雜波變多,所以導線末端務必加470uf以上的電解電容來去耦濾波,可以想象,在單片機引腳周圍1圈上也應該最好加上幾個470uf電容和104電容環繞

107.
tc4427這個mos管驅動芯片的驅動輸出腳必須直連到mos管柵極,有電阻的話容易受串擾而周期性波動吱吱聲,比如串聯200R電阻就不行

108.
大功率pcb供電線走線千萬不要在單片機周圍走線或走過單片機,一定要把大功率電源的輸入接口做在大功率電路部分的附近,避免電源線的長距離走線!,要走也不要在單片機外周走過!,且大功率pcb供電線走線要邊走線邊1路隔個幾段距離就加個104去耦電容
109.
芯片打磨去字會產生靜電;它會損毀IC的,尤其是CMOS;壞的概率很高。
我們之前的MCU都是用銼刀磨,戴上靜電環,壞的概率比較小,也可以先磨再燒,這樣只要能燒的通常是好的!

110.
.Net會自動完成垃圾回收,對于比較大的圖片,我們可以使用using關鍵字在不需要的時候自動對其進行回收
111.
多html標記的夾死比單html標記的內包塊的穩定性要好的原理,可以弄新安裝機制來防止html代碼格式不規范而導致的卸載時卸掉了不該卸的其他塊內容:

            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安裝體代碼不規范而導致卸載時時而卸少了,時而卸多了]的問題!
                {
  112.
C# winform里sql server連接串為string connectionStr = "server=服務器名;database=數據庫名;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" />
  ,不一樣的,要注意區分,不然會出現連不上問題!

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));
                    }
         ......

以上為經典的不穩定性bug的結構,如果 if (m_src_image.Width > width_for_shoujiduan)不成立則new_width=0,則引發接下來的bitmap的初始化異常,所以用于賦值用的if最好配else
114.
while ((m_WebBrowser.IsBusy || m_WebBrowser.ReadyState != WebBrowserReadyState.Complete) && aaa < 20 * 20)//不加m_WebBrowser.IsBusy 的話好像容易有最底下有1大片留白;最大等待20秒,10秒有點短
        
,然后網頁加載完時也等待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>
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:44609 發表于 2021-11-20 08:43 | 只看該作者
1.中斷和 main函數或其子函數 里不要同時有同一外設模塊信號的操作,如對74hc595的操作,對HT1621B的操作的信號線必須是有序的,而不能穿插,否則容易錯亂或花屏
2.中斷里標志位變化時,回main函數或其子函數后盡量再判斷標記位,否則容易導致遺傳上次標志位的作用,而沒有及時發現中斷返回后標志位已改變,而導致程序混亂
3.類似于信號量臨界區的穩定性處理
4.對所有死等待如等待某標志位 ,做超時退出處理,防止卡死
5.所有數組下標盡量在訪問數組前都做下標是否越界判斷
5b.做除零異常判斷,如果分母為零就不除
5c.注意:如果程序里有很多個定時器或多個高速外部中斷在工作,而且定時頻率都很高,那么如果棧空間分配很小,那么很容易發生嵌套的多層次搶占中斷而在進入中斷函數時發生局部變量分配儲存空間時失敗即堆棧溢出,而導致死機和卡死!,盡量
把功能用分支if做到1個定時器中斷里,少用定時器,并盡可能減小定時器頻率,并盡可能加大棧初始化空間,如stm32單片機里 .s 文件里的一些定義
6.對于一些潛在的不穩定因素(甚至1-2個月才出現一次故障),比如網絡忙時包ID的先后次序發生變異,而導致if xxxx.id>old.id 永遠無法再成立而卡死
7.多機通信盡量用單向通信,比如主從模式,主機發包輪詢查詢并從機返回結果模式,雙向的全雙工通信模式的穩定性比較差
8.所有按鈕處理必須做按鈕去抖動算法,甚至不是阻塞式延時式的去抖動算法,而是狀態機時的異步流程方式的過濾掉按得過快的2次按鈕事件間隔,比如200ms內再無按鈕事件且按鈕已沒按住才算按了1次,而且最好加個104電容在按鈕里并聯
9.多機主從通信盡量用 目的機ID+通信協議版本號+分包的子包ID序號(如1-10)+子包總數+本子包數據長度+上次包的全局ID+crc32校驗,方式來處理重復包,30ms內沒收到從機反饋數據則重發包(比如重發4-6次)來處理丟包,并當上次包的全局ID大于一定值時發清零包ID的cmd包來復位ID計數,但要判斷從機先收到大點的ID的包的情況
10.網口通信比usb通信穩定,usb在干擾時容易掉線
11.TF卡只適合做只讀的fat32文件系統應用,如要常寫入,最好配合 W25q64來固定分片方式的無fat32文件系統式的寫入,因為TF卡常寫入,容易寫壞文件系統格式,比如意外掉電時等
12.對于一些串口接口的模塊,如果波特率用得太大,則單片機容易串口中斷響應不過來,而發生嵌套的異常串口中斷,或丟串口數據,導致通信不穩定,波特率最好低點!
13.單片機程序的架構最好做成異步式的狀態機式的虛擬多線程方式,不要用任何阻塞式的死等待和延時,延時也要做成狀態機里的某個狀態的delay的異步非阻塞式的累加,加到一定值才進入下1個狀態碼,異步方式比阻塞式的方式的穩定性要好,處理實時性也高,多級菜單的屏幕顯示和按鍵處理也可以用狀態機式的異步流程,有個狀態碼指示當前菜單的第1級選擇項號,選擇深度,第2級選擇項號。。。。
14.相同的類似的屬于同一功能概念下的處理過程最好集成到一個函數里做分支判斷,而不要把1個功能分散到很多個函數里,這樣代碼比較難維護和二次修改
15.可以抽象為1個功能或1個流程體內的東西最好歸納法和抽象法抽象一下,不要做成if分支遍歷和窮舉所有情況方式,就像要做成矢量圖,而不是像素圖,抽象概括后的代碼寫法更有簡潔性,更
有可維護性和穩定性!
15b.復雜度盡量做成可配置數組里的數據,用配置數據來展開對代碼的控制,而不是把復雜度全部映射到代碼if分支等分支上,就像要做成矢量圖,而不是像素圖,就像一個圖形圓在可配置數組里(矢量圖里)是 yuan_type和sin cos,而若是代碼分支法,則是一堆關于圓的點陣
15c.按15點所說的抽象和歸納代碼后的代碼簡潔化和可維護性加大化,可以使代碼的條理和邏輯層次更加清晰,使更容易分析和預感到可能的bug和細節邏輯不嚴謹,從而預防bug和不穩定性bug,最終影響程序運行時穩定性,所以bug是由思維決定的

16.看門狗在程序完工后最好加上去,軟件里的關鍵數據最好做RAM區多處備份,如備份10份,死機自動復位恢復后,檢查RAM區里備份里的各備份,重復次數最高的那幾份備份(且重復次數在6次以上)最可能為原始未被破壞的數據,并做個ram標志位檢查是上電復位,還是看門狗復位,復位后可以做不清除ram數據,程序跑飛后,可以提前做些標志位判斷,如跑飛到某處,可能某標志位未被置位卻執行到這,則可以自動判斷出是已跑飛,則自動軟件復位一次!
17.
單片機芯片的最近的外圍一圈上的單片機芯片vdd,gnd引腳附近必須多弄幾個0603封裝的貼片104電容來去干擾,pcb電源類走線的線寬必須要寬,至少0.4mm以上,甚至0.8mm以上,單片機芯片在pcb局部時盡量靠近電源芯片和供電芯片處

就單單才零點幾歐電阻的導線稍微長點來傳輸電源,比如20cm長,就會導致高頻雜波變多,所以導線末端務必加470uf以上的電解電容來去耦濾波,可以想象,在單片機引腳周圍1圈上也應該最好加上幾個470uf電容和104電容環繞
;大功率pcb供電線走線千萬不要在單片機周圍走線或走過單片機,一定要把大功率電源的輸入接口做在大功率電路部分的附近,避免電源線的長距離走線!,要走也不要在單片機外周走過!,且大功率pcb供電線走線要邊走線邊1路隔個幾段距離就加個104去耦電容
18.
如果是軟件模擬spi信號接口或i2c接口,那么字節發送函數里最好先禁止總中斷使能,發完后再開啟總中斷使能,否則容易不穩定
19.
c8051f340單片機芯片工作時如果電源電壓比較低或有突然的電源脈沖浪涌干擾,容易使燒好的程序丟掉,穩定性好像不怎么好,stm32單片機芯片或GD32芯片好像就好點,就沒這個問題!,推薦優先選后者
20.
對于以太網網絡模塊芯片應用,最好把這芯片的RST復位信號接到單片機上,并RST腳接個104電容(有時候干擾容易導致意外硬式復位,而反而不穩定),這樣,網絡卡死時可以強制復位網卡芯片一次來修復
21.
lwip等網絡協議棧的某些版本有可能有內存泄露bug,或長時間運行后卡死,最好做定時自動軟件復位單片機功能,來防止內存泄露過度
22.
芯片上如果flash夠,最好做從服務器查詢新版BIN固件來自動升級單片機自身的hex固件功能(固件下載完要做CRC32校對),以及usb的固件升級功能,即net bootloader和usb bootloader,來應對【做項目即使完成和驗收后,幾個月出現的bug要修復,而措手不及】

23.
比如 休眠后喚醒時給PB1引腳1個高電平后不明原因的死機,不一定是進入未定義中斷,而是main函數之前bootloader對PB1響應:主程序進入休眠態,喚醒后 pb1為高,進入bootloader態,但bootloader里沒有重新初始化stm32 cpu的時鐘系統等,故卡死,雖然bootloader后跳轉到的main里有重新初始化stm32 cpu的時鐘系統等的代碼,但是已執行不到

24.
密腳芯片單片機等ic焊接時上松香吸錫后,如果用酒精清洗,是否過幾個月后會發生引腳間松香,助焊劑等物質發生變質而短路?而不穩定?
25.
故障實時監測功能很重要!(備災1):比如某stm32版的帶網口的板子做個板子是否在線監測和掉線時短信提醒功能,即每10秒鐘發送1個心跳包給服務器,如果服務器50秒內沒收到某板子的任何心跳包,則此板子判為已掉線,并發送報警短信,email給對應的技術員來通知處理和修復
26.
同一個安裝點上弄多個冗余板子,某塊故障時,另1塊自動切換到上線!,或雙電源,1個后備電池逆變電源,主電故障則自動切換到備電(備災2)
27.
電源接插頭不要用圓孔的那種電源插座或usb接口插座,要用專用的接線端子母座和接線端子插頭(綠色方形的帶2針的那種,最好還帶上扣機制,插緊后自動上扣),否則電源不穩或接觸不良,導致板子掉程序或其他異常,信號線也可以如此處理

28.
回流焊最后1段溫度曲線的達到最高溫度時如保持時間過長(推薦205度保持15秒,對于無鉛錫膏)導致錫膏被烤干,或錫膏過期了,就會導致出來后焊接效果不好,比如焊盤上錫的亮度不高,無光澤,太干燥,這時容易影響產品性能和加速日后的產品性能衰變
29.
eeprom寫入前不需要擦除整個扇區,比如at24c02之類的,而單片機自帶的flash和w25q64等都是flash,寫入前如果不擦除整個扇區,可能部分字節會寫錯字節,而導致異常和不穩,或者做了擦除處理,但是寫某個數據時
未備份扇區里原數據而直接擦除扇區,導致數據丟失,比如要寫入的數據數組跨越了2個扇區的邊界的情況
30.
盡量不要用一些外置的什么is62lv256+ls373方案的擴展ram內存方式,而要直接用自帶高ram大小的192K ram的單片機,比如stm32f407或stm32f103zet,stm32103re ,gd32f103cb等,因為外擴方式容易不穩定,比如受焊接質量,打板精度,環境濕度溫度,電源波動等影響
而不穩

31.
遞歸函數盡量用while/for循環+棧或數組或鏈表的方式來替代,而不要做成多層次調用函數自身方式的遞歸,做也做成帶遞歸深度變量的,限制最大遞歸深度,否則容易堆棧溢出而死機
32.
當要進行可能的后果極其危險或非常重要,不能被任何干擾誤觸發的控制操作時(比如房屋爆破隊驅動炸藥點火的裝置,自動駕駛的緊急剎車制動驅動器,高壓總閘開關繼電器),務必要備份10份以上的
控制碼,如果只有1份控制碼,當這份控制碼被強干擾信號改變內存數據而變為導致觸發的目標控制碼時,就會誤動作,而如果是10份,會校驗所有10份控制完全一致后再打開控制,這樣只有1份或2份被干擾
后就不會誤動作了,所有內存區的任何變量也都實時備份10份以上,這樣被干擾后可實時恢復正確的數據(殘留下的重復次數最多的那幾份且重復次數在6次以上即為未破壞數據),
當10份備份中重復次數在6次以下,意外著系統也無法自己自我修復,就會自動進入崩潰狀態或自動軟式復位,
再進行重要控制或驅動前,也檢查所有內存區的各變量的實時備份的10份數據,如果任何一個變量的10份備份中各數據間重復次數在6次以下,則意外著干擾太強了,系統已錯亂,則不繼續進行重要控制或進行驅動。
,進行i/o控制的代碼最好在前面多加幾百行陷井代碼和判斷是否已被干擾弄崩潰的代碼,來盡可能減少意外直接跑飛到【進行i/o控制的代碼】的概率,或者不用任何簡單的i/o來控制重要設備或高壓的重要設備,
而用i2c或spi接口的外圍驅動芯片來間接控制,且i2c或spi必須為軟件模擬,在i2c或spi的發字節函數里,每發1個比特就再判斷一次【是否已被干擾弄崩潰即檢查所有變量的10份備份是否還算完整和一致】,
這樣就非常可靠和穩定,不會被強干擾弄誤觸發了!

33.
在存在強電流,強功率,強電壓,強靜電,強射頻耦合傳遞,或可能有接觸式火花的地方盡量多的加光耦隔離來隔離信號控制計算處理部分和高壓信號驅動部分,如果一個系統上存在2個或以上獨立供電電源,
相互之間也必須完全隔離(GND等都不要接一起),用光耦耦合來傳遞控制,
繼電器盡量換為可關閉的晶閘管,或場效應管,或其他大功率開關管,這些控制開斷時有【無火花】的優點,防止繼電器通斷時的火花放電間隙導致的強干擾,如果實在要用繼電器,在繼電器的2觸點引出腳間接入RC去火花電路,
,并在繼電器的2觸點引出腳控制的真空電磁閥或電機等感應負載上反向并聯入【1個高耐壓二極管+串1個0.5歐左右的大功率電阻】,以及并聯入1個體積大的104電容,來去除真空電磁閥或電機等在斷電瞬間產生的
反向電動勢,從而消除反向電動勢在繼電器觸點剛斷開時的微小間隙里產生火花,從而消除火花產生的強干擾來使單片機跑飛,死機或復位的后果,

,另外如果用繼電器,最好每個繼電器的VCC串1個10歐的電阻后再控制繼電器,這樣繼電器吸合時對電源的脈沖干擾小點!

34.
做編程項目或電子項目,務必做一個 發現的未解決的程序bug的備忘.txt ,及時記錄發現的偶爾出現的bug,和突然靈感里閃現的預感到的可能會出現的bug情況,以及要改進的地方,
比如一些操作不變雞肋問題,穩定性問題,都要及時記錄,因為如果每忘記1點點bug,都意味著可能日后會出現這個bug,即導致項目的徹底失敗,畢竟【一個項目的成功是靠堵住所有一些可能的細節蟲洞和雞肋來做到的】
35.
如果是在別人的開放平臺或接口上做程序,那么需要定期檢查所有情況都是否正常,比如即使今天全部正常,但是他們平臺也許早就換了接口版本,只是老用戶還是維持老版本接口,新訂購的用戶用我們isv軟件時卻已切換到新接口,不能用,卻發現不了
36.
字符串數組即二維的uchar數組,在定義時,前1個方括號索引字符串序號,后1個方括號索引當前字符串的字符下標,比如u8 wavfile1n[7][15] 代表7個最長14個字符的字符串,方括號別弄反了,容易異常

37.
板子上電源處加的電解電容在1000uf以上時,容易上電時電容瞬間電流過大而導致開關電源發生短路保護,如此循環,而不易啟動

38.
EMI:pcb電源輸入部分增加共軛電感器抑制中高頻的共模噪聲,并加共模濾波電容(2個電容串聯,2端接電源正負,中間點接地)和差模濾波電容
39.
如果要抗雷擊浪涌,應在電源輸入處并聯入動作電壓大于額定電壓2-3倍以上的壓敏電阻,并且電源輸入處pcb走線寬度要粗,并電源輸入處的壓敏電阻前的走線串聯入玻璃保險管的座的相關元件
40.
pcb信號線的走線盡量短而且寬: pcb布線時選擇合理的導線寬度 由于瞬變電流在印制線條上所產生的沖擊干擾主要是由印制導線的電感成分造成的,因此應盡量減小印制導線的電感量。印制導線的電感量與其長度成正比,與其寬度成反比
晶振走線必須盡量短:           時鐘線、信號線也盡可能靠近地線,并且走線不要過長,以減小回路的環面積
信號線走線拐角應采用圓弧形:   電路板上的印制線寬度不要突變,拐角應采用圓弧形,不要直角或尖角。(9)時鐘線、信號線也盡可能靠近地線,并且走線不要過長,以減小回路的環面積。
保持環路面積最小,降低干擾對系統的影響,提高系統的抗干擾性能。并聯的導線緊緊放在一起,使用一條粗導線進行連接,信號線緊挨地平面布線可以降低干擾。電源與地之間增加高頻濾波電容
采用完整的地平面設計,采用多層板設計,鋪設地層,便于干擾信號泄放。
41.
密腳芯片懷疑存在連焊時的處理技巧1:拖焊時務必放很多松香到吸錫銅絲線上,否則容易吸錫不徹底,而留下ic引腳間不穩定性的短路問題,最后用99%濃度(不能含水多)的酒精擦去殘留的松香,并再烙鐵順引腳刮下來清理一遍酒精等

42.
盡量用全局變量或全局數組變量替代局部變量,因為進入函數時要不斷的分配局部變量,容易影響執行效率和耗用完堆棧空間等;
43.
電源部分的正常和穩定(比如電壓波紋小,高頻噪聲濾除率高,功率實足,抗突變的浪涌大電流能力強)是單片機電路和控制電路穩定工作的最重要的前
44.
像LM1086-3.3或LT1086-3.3,LMXXXXX....之類的貼片式三端穩壓器,在貼片工藝完后并過回流焊爐后,由于焊盤上印刷錫膏時錫膏量不足或者三端穩壓器引腳上翹會導致三端穩壓器過回流焊爐焊接后存在虛焊或接觸不良,
所以應該每次都手工補焊下來補點錫,否則容易不穩定,或在電子產品運輸工程中脫焊而開路
sot23封裝的貼片三級管也容易引腳上翹而導致回流焊的焊接質量不良而導致開路或虛焊(如在運輸過程中振動而脫焊)
45.
所以出廠前的24h老化測試和pcb帶電工作的高強度振動測試也很重要,來檢測是否有虛焊的現象(如在運輸過程中振動而脫焊)
46.
電源開關等不要用機械式的單刀雙擲式的那種微型撥動開關,很容易損壞或接觸不良,最好做成自鎖式的那種按鈕式電源開關或船型開關

47.
注意場效應管的前置驅動電壓放大級用的三極管的發射極最好是接地形式的而場效應管G端接三極管的集電極再1個上拉電阻上拉到比如24V,否則如果NPN的發射極接G端時,當三極管導通,
發射極的電壓最大也只有基極的電壓的大小,將無法良好的使場效應管完全導通,比如IRF540N的Vgs需要10V左右才完全導通,另外應該在G端對地并聯1個10V穩壓管,因為一般Vgs也不能太大,20V左右最大了
不要用單片機IO口直接驅動場效應管,因為一般場效應管Vgs在10V左右才能完全導通和飽和,單片機io口電壓不夠,會驅動不良
48.
光耦存在壽命,所以盡量用非光耦的取代元件來做隔離,比如一些ic:
光耦中的發光二極管隨著時間推移而老化;比如開關電源反饋環路中光耦老化,傳輸比下降后可能引發故障。
不能用線性光耦作為模擬量傳感:通光耦產品手冊中對電流傳輸比只給出一個大概的范圍(從低到高可以差別一倍),而且沒有給出溫漂和老化的指標。用于開環傳輸模擬量,精度是得不到保證的,所以導致過壓、過流,以致損壞
49.
74hc595的時鐘和數據線上要加阻容濾波的  這類IC  速度太高   對毛刺干擾特別敏感,而導致誤動作或屏幕錯亂
49a.
做電壓檢測電路時,比如測220V電壓或380V電壓,不要用線性光耦+1M電阻來降壓采樣,而要用微型工頻變壓器(鐵芯那種(非高頻磁芯))來降壓并整流采樣,因為光耦會老化和被擊穿(雷擊等意外),或有溫飄
50.
不要在任何中斷函數里調用1個需要等待另1種中斷函數來置位目標所需標志位才能結束的函數或代碼段,否則可能因為中斷優先級的關系而導致這另1種中斷函數永遠無法執行而卡死在等待里,導致死機
51.
謹慎使用keil自帶的庫函數之sprintf和printf,容易出問題,比如卡死或內存泄露
52.
LQFP64/LQFP48之類的密腳IC如果用拖焊法手動焊接,那么如果芯片在pcb上的每個引腳的焊盤的長度如果比較長,則很容易拖焊后在引腳的焊盤的靠芯片內部方向處產生錫的殘留而導致2個引腳焊盤間的短路,所以
此法不穩定,還是用鋼網刷錫膏+回流焊法比較可靠!

53.
ad采樣均值濾波法時應該用:
        
            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)個字節,而不是(junzhi_lvbo_count-1)個

54.
數字電源或開關電源的mos管的驅動級的信號輸入最好帶一級最大高電平時間限制的硬件式電路,比如最大允許高電平500us,來防止卡死時一直高電平而使mos一直導通而燒壞mos或高頻變壓器!

55.
注意臨界大小判斷時容易出現的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,那么就會陷入死循環,永遠無法達到max_zhankongbi1,而且不是只離max_zhankongbi1一點,容易使調節環在誤差比較大時卡死
,所以要加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.
數字電源的每次反饋調整pwn或每次pid調整時的調整間隔周期必須在相隔500us左右以上而不是1個while死循環里不停的調整,
而要用timer定時定間隔的調(所以輸出級的電解電容務必2200uF以上,來延長電壓突變緩沖時間),而且每次調整的誤差比例乘積因子必須要小,否則容易輸出電壓波紋很大!
控制輸出電壓波紋的關鍵是這500us內不斷的采樣adc(可多次DMA式adc)來累加并在500us結束時取累加值的平均值來數字濾波,因為一般ad采樣電壓的波紋比較大

57.
keil中 3.11/6.1會被當做小數來計算結果,如果是3/23,則結果為0,因為被當做整數來處理,所以必須寫為(float)3.0/(float)23.0
58.
如果需要動態改變單片機定時器的定時周期,那么要注意周期定時值不能被改的太小,否則會導致不斷的高速的進入定時中斷而卡死,要判斷和限制下

59.
【bug調試和發現bug原因,解決問題現象】的原則是【在相對系中尋找局部絕對系來作為支撐點,進而慢慢全部覆蓋問題機制】,比如找幾個確定點和可以差量法,對比法確定的程序現象和機理,然后再慢慢消元,減少變化量的個數,盡量避免多個量
一起在變的問題框架,因為變量1多,要測試的樣本就成指數級增長!

60.
c#上位機程序防卡頓經驗1:
阻塞式的任務和代碼要全部在后臺線程里獨立運行,而不能在主界面的主線程里執行,對于后臺線程里要跨線程操作主界面控件的,這段操作界面的代碼要用到Invoke,相當于卡死界面一點時間,所以在
Invoke里調用的 Action<string> actionDelegate3 = (x) =>{。。。。}之類的里的代碼盡量執行時間耗時極短,比如盡量把界面控件某狀態的死等待代碼不要放在Invoke的執行體代碼里,
比如 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }這段等待webbrowers加載網頁完畢的代碼不要放在Invoke的執行體代碼里,而放在
線程代碼里,即Invoke的執行體代碼執行完后的緊接著的下一句代碼再執行 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }
,而Invoke的執行體代碼里如果執行webbrowers1.Navigate(aaa1.url);之類的,也會在打開網頁時卡頓一段時間界面,所以盡量改為異步方式的Navigate函數,比如BeginNavigate()之類的
61.
C#里等待webbrowser是否加載網頁完成的穩定可靠的檢測方法為:延時式再判斷累計法(注意:線程里讀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秒內都無Busy狀態再出現,那么極有可能網頁和其所有網頁子框架都已加載完成
                          {
                              break;
                          }

                      }

62.
c#里容易異常的地方和一些界面控制代碼外圍盡量包1層try ...catch,來防止崩潰出錯
63.
c#里object 不能直接隱式轉為int,而要先tostring()后再int.parse?

64.
js里的ajax里提交后盡量加&r="+Math.random()后綴來防止緩沖而不更新
65.
路徑可能變動的網頁里的帶背景img定義的css盡量不要放在網頁里,而放在一個外部css文件里,這樣圖片可以相對css文件來定義路徑
66.
對于ajax點擊鏈接執行函數但不跳轉,建議不要用href:void(0)...+onclick(兼容性不好,有些瀏覽器異常打開新窗口),而用div+onclick+div加手型鼠標css
,而且onclick所在div要顯式定義好height和width,否則容易兼容性不好,而有的瀏覽器點擊無響應!
Example:CSS鼠標手型效果 <a href="#" style="cursor:hand">CSS鼠標手型效果</a>
Example:CSS鼠標手型效果 <a href="#" style="cursor:pointer">CSS鼠標手型效果</a>
注:pointer也是小手鼠標,建議大家用pointer,因為它可以兼容多種瀏覽器。

67.
js里或帝國cms里esetcookie('last_xianhua'.$pinlun_id,$time1,time()+3600,0);的設置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發送1次模擬按鍵是為了觸發input輸入框的onchange,而最終還是強制用.SetAttribute("value", arr1[uidto_index].Trim());來正確的設置值,防止SendKeys.SendWait發送時丟字符(比如界面很卡時或手動轉移輸入焦點就容易丟)
,即SendKeys.SendWait+ .SetAttribute("value", 結合法
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.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.
各項都改好和文件都復制好了,還是故障依舊,那么很可能問題不在文件本身,而在文件的訪問權限給對應的服務進程沒賦予權限,比如:php怎么也加不入mysql組件,php.ini和庫,dll擴展都復制了也一樣,此時很可能是system32和外面的php.ini,庫等的安全屬性沒賦予iis賬戶和network等

71.
穩定性還含過熱保護穩定性:如果pcb設計上含帶過熱保護的大電流芯片,比如THB7128或者一些開關電源的過熱保護,所以出廠前要設置最合適的THB7128工作電流來使連續工作1周或更長都不會發生過熱保護而罷工
72.
對于大功率的處理器或單片機,比如DSP,ARM9-11等,發熱量很大的,那么必須加風扇散熱,否則cpu的高溫度會導致運行速度下降和更容易程序跑飛,從而導致死機和崩潰!:比如一些路由器的死機很可能是散熱不好,沒加風扇,或電源部分用的老式的變壓器式
的低輸出電流且容易受電網電壓欠壓影響輸出電壓的電源,而不是強悍低波紋且穩壓范圍廣的開關電源
優化散熱+自動重啟自身(用于解決可能的內存泄露等)可徹底穩定路由器:
大多數的路由器散熱都做得不是太好,尤其是CPU;
我的一個N年前的NW618 經過改造后,連續無障礙運行了4、5年了;
首先是主要發熱部件的散熱,車間里找銅皮、銅板——最好是紫銅,然后做好防短路處理,手工折好形狀——也是個稍微考驗腦子的活,最好是高度剛好在扣上蓋后能牢牢壓住;
NW618可以刷固件,刷了個番茄的;
設置定時自動重啟;
別管多熱,這個路由器都沒給我填過麻煩;
路由器、貓 絕大多數的散熱都很差,所以,提醒各位注意降溫;
我是拆了3個以上的無線路由器后才發現的…… ;
73.
div+css模板里div塊間的分界html注釋是div里的table化,有利于節約閱讀情緒成本

74.
伺服電機速度響應:加速區才幾ms而一般步進要100-200ms+最好用柔性聯軸器(因為加速區時扭矩很大)

75.
伺服電機套裝的伺服電機簡易買無刷的3線接口的直流無刷電機 ,有刷的電機不耐用
76.
數碼管熄滅時再做ADC,比如4位數碼管顯示,分5個時間隙,假如2ms一位,則前面8ms顯示,然后關閉顯示2ms后做ADC,避免LED電流對ADC的影響。外掛的ADC是因為LED的電影響不到它。
ADC部分沒有獨立的模擬地和電源的MCU,就要這樣考慮。
77.
實踐證明:smt中,貼片式TF卡座過回流焊后很容易虛焊(焊腳上翹現象),所以每次必須手工補焊一次
78.
實踐證明:部分旋擰方式的可調電阻內部有些是開路的,壞的,必須出廠測試
79.
不用【多層if或[多個單層if+return]】實現逐層條件判斷的方法: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任務切換調用中斷的優先級必須最低,防止實時任務嵌套中斷被任務切換中斷所打斷而異常!!!
82.
freertos里的
#ifndef traceMALLOC
    #define traceMALLOC( pvAddress, uiSize )
#endif
#ifndef traceFREE
    #define traceFREE( pvAddress, uiSize )
#endif
可以用于內存泄露的內存分配和回收的對稱性檢測,lwip里應該也有類似的機制
83.
freertos里采用heap_4.c這個內存管理方案來編譯進:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 20 * 1024 ) ) //采用heap_4.c才有此選項,即不交給keil的malloc函數來分配內存,而由系統管理

84.
如果數據傳輸要不斷對新連接分配臨時內存區,那么不定長的分配的和回收而留下內存空間間隙的話,久而久之會導致太多的內存碎片,是否最后導致性能越來越慢,甚至內存分配失敗而卡死?,如lwip?,所以單片機內存分配簡易采用數組劃分定長分配內存區的策略才穩定!
,配合configMINIMAL_STACK_SIZE設置夠大來使定長分配內存,而減少內存碎片積累
85.
Heap_Size       EQU     0x00010000  ;64k作為堆大小,即用于freertos的malloc分配內存,來使http_connect_task連接不會內存分配失敗,配合configMINIMAL_STACK_SIZE設置夠大來使定長分配內存,而減少內存碎片積累
86.
總線頻率要留有余量,比正常能驅動外設的最高頻率還稍微低點,才能穩定:
    FSMC_NORSRAMTimingInitStructure.FSMC_CLKDivision =12;//2分頻的話驅動dm9000a不起來,總線頻率太快!!,降為12分頻就可以

87.
如果非要用usb轉串口方案,穩定性和兼容性比較:FT232>PL2303雙芯片>ch340,如何查某種usb轉串口線所采用的芯片型號?:設備管理器里查設備屬性的VID和PID法
   
88.
有些以太網網絡芯片要做斷線檢測并自動重連,否則很容易斷線后徹底卡死無法收發數據,比如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.
注意單句編程語句寫法和次序不同而導致的穩定性不同,如:
     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#等對于一些多線程用的組件,在線程主入口函數里的末尾務必加while(true){sleep(10);}之類的死循環來卡住,否則 線程主入口函數里新建立的子線程會因為線程主入口函數返回了而沒卡住,而在不久后會全部注銷子線程和變量,而導致一些運行一段時間后莫名其妙的exe退出現象!
91.
if((args.data[1]&128)==128)與 if(args.data[1]&128==128)可能有區別,因為可能==優先于邏輯與&,所以  if(args.data[1]&128==128)可能會變為  if(args.data[1]&(128==128)),即 if(args.data[1]&1)
92.
按照我做開關電源的經驗,八成是變壓器出問題了,一個開關電源里面的所有器件的參數都比較可控,比如耐壓、耐流、功率、容值、阻值等等,唯一難以把控的來料就是變壓器,每批的參數都會有偏差,找找沒炸雞的電源,測試一下MOS上面的尖峰電壓吧,最有可能就是變壓器漏感超標導致尖峰電壓過高擊穿MOS,造成連串的燒毀!:換更高耐壓的mos管才是根本解決辦法,比如IRF540N換為IRF640N
         
93.
程序防破解的原理:【可利用的特征的消滅技術,加大破解時間成本】:運用【變中有變】原理:在原程序正常功能的變量變化鏈之中再套入1層變化,而這層變化是防破解用的,破解者無法識別是正常變化還是防破解用的變化,除非理解了我的程序,但是如果有理解能力,自己都能寫代碼,對這個矛盾加以利用即可:
,如正常代碼功能里對uuu[1]的值進行判斷和分支,但是我們如果識別到軟件未注冊,那么就可以在判斷未注冊后,不做簡單的返回,而是對uuu[1]的值取些非正常分支判斷需要的值
94.
數字電源板開關頻率可以設到36khz,不要15khz,15khz容易在負載功率波動時輸出電壓不穩!,而64khz又容易使輸出級整流管過熱!
95.
不要在中斷函數里做網口發包 ,usb發包,串口發包等數據傳輸,這些數據傳輸代碼要全部做在main里單線程化,然后中斷函數里用flag標志位來指引main里的數據傳輸代碼開始工作,這樣才穩定!
96.
以前比如加速曲線有加速到加速曲線數組上標就停止再加速的bug:改為【加速曲線數組走到后也不停止加速,而是加速到最大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)){//加速曲線數組走到后也不停止加速,而是加速到最大kHz1才開始勻速
                                             
                                x_cur_jiasuqu_max_val_reached=1;  //v14
   
                              x_cur_ruanqidong_ed=1;//勻速區
                             }
                           
                           
                           
         }

                              
97.
1個應用軟件在操作系統里所分配到的cpu資源是有限的,所以應用軟件里的線程里應該盡量避免while+sleep死循環方式的等待某事件,這樣很耗cpu,導致軟件很卡頓或不流暢,盡量用while+多線程的信號量機制來解決,如  _autoResetEvent_UDPsend.WaitOne(5);//must 5,因為這樣萬一因為某種不穩定而卡住也就只卡5ms
98.
數字開關電源板的高頻變壓器的次級需要比正常圈數多繞2圈,留點升壓空間,來防止變壓器變壓比達到最大而飽和而無法繼續隨占空比變大而升壓,進而導致的mos管在占空比飆升后燒毀!,另外,程序里設定的輸出穩壓值要比正常額定輸出值低2-3伏,也是為了防止變壓器變壓比達到最大而飽和而
無法繼續穩壓而導致的mos管經常燒毀!

99.
float fabs_c(float f1){
   
   
    if(f1>=0){
        
        return f1;
        
    }else{
        
        
        return f1*(-1);
        
    }
   
   
   
   
   
   
}

typedef struct PID
{
//int SetPoint; //設定目標  Desired Value
double Proportion; //比例常數  Proportional Const
double Integral; //積分常數  Integral Const
double Derivative; //微分常數  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 參數初始化
//*****************************************************
void IncPIDInit(void)
{
sptr ->DE=0;
sptr ->LastError = 0; //Error[-1]
sptr ->LastDE = 0; //Error[-2]
sptr ->Proportion = P_DATA; //比例常數  Proportional Const
sptr ->Integral = I_DATA; //積分常數 Integral Const
sptr ->Derivative = D_DATA; //微分常數  Derivative Const
}
//*****************************************************
//增量式 PID 控制設計
//*****************************************************
float IncPIDCalc(float NextPoint,float SetPoint )
{
    float iError, iIncpid; //當前誤差
    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 參數初始化
//*****************************************************
void IncPIDInit2(void)
{
sptr2 ->DE=0;
sptr2 ->LastError = 0; //Error[-1]
sptr2 ->LastDE = 0; //Error[-2]
sptr2 ->Proportion = P_DATA; //比例常數  Proportional Const
sptr2->Integral = I_DATA; //積分常數 Integral Const
sptr2 ->Derivative = D_DATA; //微分常數  Derivative Const
}
//*****************************************************
//增量式 PID 控制設計
//*****************************************************
float IncPIDCalc2(float NextPoint,float SetPoint )
{
    float iError, iIncpid; //當前誤差
    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序列號式確認流的穩定性永遠大于布爾變量式的確認法(比如收到確認包就置位)
==================================================================================
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));
的區別,
                                 
以及

(u16)(((float)1000000.00)/ (float)a_zhou_mutli_soft_timer_hz);

(u16)(((float)1000000.00)/ ((float)a_zhou_mutli_soft_timer_hz));
的區別,
以及
(byte)(x_diff_in_mcu1 >> 8 & 0xff);

(byte)((x_diff_in_mcu1 >> 8) & 0xff);
的區別,
容易造成bug

==================================================================================

103.
有時重新構架(去掉各種不穩定式機制后的新構架)比在老架構里找問題要來得快
==================================================================================
104.
單片機多定時器應用代碼里,要注意可能不同定時器所用的主頻不同,比如stm32f407的APB1, APB2等上的不同定時器,別搞亂了

105.
服務器使用的雙滾珠風扇,4040 的12V0.3A,接通電源之后噪音會比較大,但卻是不想使用4010的南北橋風扇,不是滾珠的,有不轉的風險。

106.
就單單才零點幾歐電阻的導線稍微長點來傳輸電源,比如20cm長,就會導致高頻雜波變多,所以導線末端務必加470uf以上的電解電容來去耦濾波,可以想象,在單片機引腳周圍1圈上也應該最好加上幾個470uf電容和104電容環繞

107.
tc4427這個mos管驅動芯片的驅動輸出腳必須直連到mos管柵極,有電阻的話容易受串擾而周期性波動吱吱聲,比如串聯200R電阻就不行

108.
大功率pcb供電線走線千萬不要在單片機周圍走線或走過單片機,一定要把大功率電源的輸入接口做在大功率電路部分的附近,避免電源線的長距離走線!,要走也不要在單片機外周走過!,且大功率pcb供電線走線要邊走線邊1路隔個幾段距離就加個104去耦電容
109.
芯片打磨去字會產生靜電;它會損毀IC的,尤其是CMOS;壞的概率很高。
我們之前的MCU都是用銼刀磨,戴上靜電環,壞的概率比較小,也可以先磨再燒,這樣只要能燒的通常是好的!

110.
.Net會自動完成垃圾回收,對于比較大的圖片,我們可以使用using關鍵字在不需要的時候自動對其進行回收
111.
多html標記的夾死比單html標記的內包塊的穩定性要好的原理,可以弄新安裝機制來防止html代碼格式不規范而導致的卸載時卸掉了不該卸的其他塊內容:

            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安裝體代碼不規范而導致卸載時時而卸少了,時而卸多了]的問題!
                {
  112.
C# winform里sql server連接串為string connectionStr = "server=服務器名;database=數據庫名;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" />
  ,不一樣的,要注意區分,不然會出現連不上問題!

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));
                    }
         ......

以上為經典的不穩定性bug的結構,如果 if (m_src_image.Width > width_for_shoujiduan)不成立則new_width=0,則引發接下來的bitmap的初始化異常,所以用于賦值用的if最好配else
114.
while ((m_WebBrowser.IsBusy || m_WebBrowser.ReadyState != WebBrowserReadyState.Complete) && aaa < 20 * 20)//不加m_WebBrowser.IsBusy 的話好像容易有最底下有1大片留白;最大等待20秒,10秒有點短
        
,然后網頁加載完時也等待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>

評分

參與人數 1黑幣 +13 收起 理由
fj51hei + 13 很給力!

查看全部評分

回復

使用道具 舉報

板凳
ID:262 發表于 2021-11-26 04:59 | 只看該作者
好資料,51黑有你更精彩!!!
回復

使用道具 舉報

地板
ID:275111 發表于 2021-12-10 12:06 | 只看該作者
MARK一下 就是這個文檔的寫法比較暴力。
回復

使用道具 舉報

5#
ID:712493 發表于 2024-4-27 08:54 | 只看該作者
這個必須收藏!!
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产精品乱码一区二区三区 | 超碰一区二区 | 粉嫩国产精品一区二区在线观看 | 亚洲综合精品 | 精品综合视频 | 久草资源在线视频 | 中文字幕成人 | 日韩在线观看中文字幕 | 久久国产免费看 | 国产黑丝av | 国产精品电影在线观看 | www.玖玖玖 | 91污在线 | 欧美日韩中文字幕在线 | 国产电影一区二区三区爱妃记 | 欧美极品在线 | 欧美色综合一区二区三区 | 中文字幕高清av | 四虎成人精品永久免费av九九 | 成人精品视频在线观看 | 亚洲成人黄色 | 国产一二三区免费视频 | 日日干日日色 | 草久久 | 日韩二区三区 | 三级黄色片在线观看 | 999免费视频 | 中文精品视频 | 99在线播放 | 国产精品日日摸夜夜添夜夜av | 久久精品亚洲欧美日韩久久 | 色综合久久久 | 不卡一区 | 婷婷不卡 | 一区二区三区免费 | 日韩成人一区 | 精产嫩模国品一二三区 | 天天综合久久 | 一区二区三区四区电影视频在线观看 | 日韩国产欧美 | 日韩精品一区在线 |