找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1629|回復(fù): 11
收起左側(cè)

一個非常詭異的單片機(jī)程序調(diào)試問題

[復(fù)制鏈接]
ID:997026 發(fā)表于 2022-2-16 13:25 | 顯示全部樓層 |閱讀模式
本帖最后由 hxdby 于 2022-2-16 19:07 編輯

最近在調(diào)試中遇到一個非常詭異的問題!雖然我解決了,但是到現(xiàn)在我都不知道具體原因是啥, 特分享出來,與大家探討。

做了一個簡單的可以通過按鍵調(diào)整時間控制繼電器打開關(guān)閉的單片機(jī)板子,帶數(shù)碼管顯示,全部功能程序代碼都已經(jīng)完成,昨天板子我開了一夜做測試,今天早上起來突然發(fā)現(xiàn)一個鍵突然失靈了,不能切換分秒顯示了,我以為是按鍵壞了,結(jié)果不是。

先解釋板子的工作邏輯:1,板子帶一路繼電器,可以通過按鍵設(shè)置分和秒來設(shè)置繼電器打開和關(guān)閉的時間
2,繼電器打開時間設(shè)置函數(shù)是RELAY_SET_ON(), 繼電器關(guān)閉時間設(shè)置函數(shù)是RELAY_SET_OFF(); 這兩個函數(shù)實(shí)現(xiàn)功能和代碼都一樣,只是變量名不同
3,設(shè)置時間可以通過數(shù)碼管顯示,
4,總共三個按鍵,SW1, SW2, SW3。SW1是增加,SW2是減小。SW3是分和秒設(shè)置切換按鍵。簡單說就是,如果設(shè)置分鐘,按一下SW1,分鐘增加1,按一下SW2,分鐘減小1,如果要設(shè)置秒鐘,則按一下SW3,切換到秒鐘設(shè)置,然后在通過SW1/SW2來增減。

先上部分程序(不重要的單片機(jī)代碼就不貼出來,只貼重要的):

//全局變量

uint8_t    relay_on_cnt =0; //打開時間設(shè)置中的分秒切換變量,通過SW3設(shè)置
uint8_t    relay_off_cnt =0; //關(guān)閉時間設(shè)置中的分秒切換變量,通過SW3設(shè)置
uint8_t    RELAY_min_set_ON =0 //繼電器打開時間設(shè)置中的分鐘變量
uint8_t    RELAY_sec_set_ON = 0 //繼電器打開時間設(shè)置中的秒鐘變量

uint8_t    RELAY_min_set_OFF =0  //繼電器關(guān)閉時間設(shè)置中的分鐘變量
uint8_t    RELAY_sec_set_OFF = 0 //繼電器關(guān)閉時間設(shè)置中的分鐘變量


uint8_t    key_number3 =0; //觸發(fā)的按鍵編號,當(dāng)SW3按下時,按鍵掃描KEY_SCAN()函數(shù)返回不同的key_number3鍵值
uint8_t    key_number =0; //觸發(fā)的按鍵編號,當(dāng)SW1 或SW2按下時,key_number返回不同的鍵值



//函數(shù)原型

void  RELAY_SET_ON(); //設(shè)置繼電器打開時間
void  RELAY_SET_OFF(); //設(shè)置繼電器關(guān)閉時間
void  KEY_SCAN() ;  //按鍵掃描
void  DISPLAY_MIN_SEC_SET();  //數(shù)碼管顯示設(shè)置的分秒?yún)?shù)

//主函數(shù)

int  main (void)
{
   while(1)
{
  RELAY_SET_ON();
  RELAY_SET_OFF();
  KEY_SCAN() ;
}


}




// 函數(shù)定義
void  RELAY_SET_ON();  //設(shè)置繼電器打開時間

{
     switch (key_number3) //SW3按下
        {
        case 1:  //SW3短按,relay_on_cnt計(jì)數(shù)器在0和1之間循環(huán)自加
        relay_on_cnt ++;
        if (relay_on_cnt >1)
        {
                relay_on_cnt=0;
        }
        key_number3=0;
        break;
        
        case 2:
        relay_on_cnt --;  //SW3長按超過1s,relay_on_cnt計(jì)數(shù)器在0和1之間循環(huán)連續(xù)自減,實(shí)現(xiàn)連擊功能
        if (relay_on_cnt>1)
        {
                relay_on_cnt=1;
        }
        key_number3=0;
        break;
        
        default:; break;
        }
/*以上的代碼主要是通過按下SW3來切換分鐘和秒鐘的設(shè)置,如果 relay_on_cnt=0,則進(jìn)入分鐘設(shè)置界面。如果 relay_on_cnt=1,則進(jìn)入秒鐘設(shè)置界面,這段代碼和RELAY_SET_OFF();中完全一樣,但是這里沒問題,RELAY_SET_OFF();會出現(xiàn)問題*/      


       if (relay_on_cnt==0)  //繼電器打開分鐘設(shè)置
        {
                switch (key_number)  //根據(jù)返回到鍵值進(jìn)行分鐘的加減操作
                {
                        case 1:  // SW1 觸發(fā),開始減操作
                        RELAY_min_set_ON --;
                        if (RELAY_min_set_ON >99)  
                        {
                                RELAY_min_set_ON =99;
                        }
                        
                        key_number=0;
                        
                        break;
                        
                        case 2: //SW2觸發(fā),開始加操作
                        RELAY_min_set_ON ++;
                        if (RELAY_min_set_ON >99)
                        {
                                RELAY_min_set_ON =0;
                        }
                        
                        key_number=0;
                        
                        break;
                        
                        default: ; break;
                }        
        }


         if (relay_on_cnt==1) //秒設(shè)置
        {
                        switch (key_number)
                        {
                                case 1:  // SW1 觸發(fā),開始減操作
                                RELAY_sec_set_ON --;
                                if (RELAY_sec_set_ON >99)  
                                {
                                        RELAY_sec_set_ON =99;
                                }
                                
                                key_number=0;
                                
                                break;
                                
                                case 2: //SW2觸發(fā),開始加操作
                                RELAY_sec_set_ON ++;
                                if (RELAY_sec_set_ON >99)
                                {
                                        RELAY_sec_set_ON =0;
                                }
                                
                                key_number=0;
                                
                                break;
                                
                                default: ; break;
                        }
               
        }
        
        DISPLAY_MIN_SEC_SET();  // 設(shè)置第參數(shù)在數(shù)碼管即時顯示        


}



//函數(shù)定義
void  RELAY_SET_OFF();  //設(shè)置繼電器關(guān)閉時間,該函數(shù)代碼與上述RELAY_SET_ON()中完全相同,只是變量名不同. 所以不再一一注釋

{
     switch (key_number3)
        {
        case 1:
        relay_off_cnt ++;
        if (relay_off_cnt >1)
        {
                relay_off_cnt=0;
        }
        key_number3=0;
        break;
        
        case 2:
        relay_off_cnt --;
        if (relay_off_cnt>1)
        {
                relay_off_cnt=1;
        }
        key_number3=0;
        break;
        
        default:; break;
        }
        //上述紅色部分代碼出了問題。〕鰡栴}后,檢查了這部分代碼,和RELAY_SET_ON();中的完全一樣,后來復(fù)制了RELAY_SET_ON();中的這部分代碼到這里,改了變量名,問題就解決了!覺得很詭異,如果是這部分代碼出問題,那么在一開始就應(yīng)該有問題,而我的板子是跑了一夜之后出問題的,而且是100%必現(xiàn),我仔細(xì)檢查了代碼,完全一樣,手動修改某些變量,也不行。非得從RELAY_SET_ON();中復(fù)制才可以,why?????


       if (relay_off_cnt==0)
        {
                switch (key_number)
                {
                        case 1:  // SW1 觸發(fā),開始減操作
                        RELAY_min_set_OFF --;
                        if (RELAY_min_set_OFF >99)  
                        {
                                RELAY_min_set_OFF =99;
                        }
                        
                        key_number=0;
                        
                        break;
                        
                        case 2: //SW2觸發(fā),開始加操作
                        RELAY_min_set_OFF ++;
                        if (RELAY_min_set_OFF >99)
                        {
                                RELAY_min_set_OFF =0;
                        }
                        
                        key_number=0;
                        
                        break;
                        
                        default: ; break;
                }        
        }


         if (relay_off_cnt==1) //秒調(diào)整
        {
                        switch (key_number)
                        {
                                case 1:  // SW1 觸發(fā),開始減操作
                                RELAY_sec_set_OFF --;
                                if (RELAY_sec_set_OFF >99)  
                                {
                                        RELAY_sec_set_OFF =99;
                                }
                                
                                key_number=0;
                                
                                break;
                                
                                case 2: //SW2觸發(fā),開始加操作
                                RELAY_sec_set_OFF ++;
                                if (RELAY_sec_set_OFF >99)
                                {
                                        RELAY_sec_set_OFF =0;
                                }
                                
                                key_number=0;
                                
                                break;
                                
                                default: ; break;
                        }
               
        }
        
        DISPLAY_MIN_SEC_SET();
        


}

現(xiàn)在的問題是,板子跑了一夜之后,繼電器關(guān)閉時間設(shè)置突然不顯示了,數(shù)碼管全黑。而繼電器打開設(shè)置界面是正常顯示的。我關(guān)閉板子電源,重新開機(jī),繼電器關(guān)閉時間設(shè)置界面可以顯示,但是只要一按SW3按鍵切換分秒設(shè)置,就沒顯示了,而且這個問題是100%必現(xiàn)。

我懷疑是RELAY_SET_OFF(); 中的程序出了問題,所以和RELAY_SET_ON(); 對比了一下,發(fā)現(xiàn)完全一樣,除了變量名不同,其他沒有任何區(qū)別。而且這個問題不是一直出現(xiàn)的,是板子開始是好的,我開機(jī)一夜,跑了一夜后突然變成這樣的。如果是程序問題,應(yīng)該從一開就會有問題。

我百思不得其解,因?yàn)槿绻荝ELAY_SET_OFF();程序問題,那RELAY_SET_ON();一定會出現(xiàn)問題,因?yàn)檫@兩個函數(shù)中的代碼邏輯完全相同,只是變量名不同,這套代碼也用了好幾月了,沒出過問題。但現(xiàn)在的問題是,兩個完全一樣的函數(shù),繼電器打開時間設(shè)置顯示沒有任何問題,而且問題只出在按下SW3按鍵的時候。

于是我開始研究SW3這一段代碼,也就是上述RELAY_SET_OFF();中紅色部分,我對比了幾百次,和RELAY_SET_ON();完全一樣,因?yàn)楸旧硪矝]有多少代碼。我突發(fā)奇想,把RELAY_SET_ON();中對應(yīng)部分代碼復(fù)制過來,然后改掉變量名,然后燒錄測試,板子居然好了!

我實(shí)在不理解為啥會這樣?非得復(fù)制才行?我覺得不可理解,究竟是啥原因,有沒有高人可以深入解釋下,感謝!

回復(fù)

使用道具 舉報(bào)

ID:213173 發(fā)表于 2022-2-16 17:32 | 顯示全部樓層
代碼有點(diǎn)長,粗略看了一下,無非就是個可調(diào)定時開關(guān)。程序夠架不太合理,最關(guān)鍵的是main函數(shù)沒有約束在while(1)中運(yùn)行,隨時都有可能跑飛。
回復(fù)

使用道具 舉報(bào)

ID:997026 發(fā)表于 2022-2-16 19:08 | 顯示全部樓層
wulin 發(fā)表于 2022-2-16 17:32
代碼有點(diǎn)長,粗略看了一下,無非就是個可調(diào)定時開關(guān)。程序夠架不太合理,最關(guān)鍵的是main函數(shù)沒有約束在whil ...

不好意思,while(1)原程序中肯定有的,貼程序的時候漏掉了。
回復(fù)

使用道具 舉報(bào)

ID:311903 發(fā)表于 2022-2-16 19:30 | 顯示全部樓層
代碼太亂不想看,但是你可以試試編譯的時候全部文件重新編譯
回復(fù)

使用道具 舉報(bào)

ID:213173 發(fā)表于 2022-2-16 19:42 | 顯示全部樓層
hxdby 發(fā)表于 2022-2-16 19:08
不好意思,while(1)原程序中肯定有的,貼程序的時候漏掉了。

把你的確切需求和硬件環(huán)境詳細(xì)講清楚,有空給你寫一個示例。
回復(fù)

使用道具 舉報(bào)

ID:429003 發(fā)表于 2022-2-17 16:13 | 顯示全部樓層
代碼難得看,但從你描述來看,問題應(yīng)該不是程序的問題。估計(jì)是單片機(jī)FLASH某個位置出了點(diǎn)故障了,不能置0又或者不能至1。如果這個板子燒寫你發(fā)生故障前的代碼每次必定出現(xiàn),那么基本可以確定就是FLASH故障了。

你稍微調(diào)整一下代碼,編譯后燒寫文件發(fā)生了變化,恰巧與故障點(diǎn)位的值相符,也就巧妙的避開這一問題。
回復(fù)

使用道具 舉報(bào)

ID:429003 發(fā)表于 2022-2-17 16:25 | 顯示全部樓層
另外,你的設(shè)計(jì)是用單片機(jī)控制繼電器,是否繼電器控制線圈未設(shè)計(jì)續(xù)流二極管?
如果沒有續(xù)流二極管,這樣繼電器線圈斷開瞬間的感應(yīng)電流無處泄放,線圈兩端會產(chǎn)生很高的感應(yīng)電壓,這個感應(yīng)電壓串入你的單片機(jī)系統(tǒng),那就很有可能燒壞一些器件,單片機(jī)FLASH被燒壞一些位置,也不是沒有可能。
回復(fù)

使用道具 舉報(bào)

ID:584814 發(fā)表于 2022-2-17 17:19 | 顯示全部樓層
有些問題看似莫名其妙的,可能是程序中有肉眼不可見的啥,也可能硬件里出現(xiàn)了個啥;
有些問題真的莫名其妙的,曾經(jīng)調(diào)試程序時只在中間加了個空行(多個回車)就完美解決問題……
別以為單片機(jī)個個都是完美的,編譯環(huán)境環(huán)境也全都是完美的......Bug無處不在
個人感覺,經(jīng)驗(yàn)就是不斷試錯,物理學(xué)上的成功有時未必都能講出道理。
先按書上寫的辦,畢竟是眾人的經(jīng)驗(yàn)集成,廣告可以看但別全信;
不行后按自己的想法去干,成了有機(jī)會多驗(yàn)證再發(fā)展,又可能成長出一代宗師
回復(fù)

使用道具 舉報(bào)

ID:997026 發(fā)表于 2022-2-17 17:38 | 顯示全部樓層
xstong 發(fā)表于 2022-2-17 16:25
另外,你的設(shè)計(jì)是用單片機(jī)控制繼電器,是否繼電器控制線圈未設(shè)計(jì)續(xù)流二極管?
如果沒有續(xù)流二極管,這樣繼 ...

感謝回復(fù)。
繼電器端加了足夠的保護(hù)電路,包括續(xù)流二極管,壓敏電阻,二極管用的是1N4007反向電壓1000V,壓敏電阻并接在繼電器觸點(diǎn)端消電弧。所以應(yīng)該不是繼電器的問題。
回復(fù)

使用道具 舉報(bào)

ID:997026 發(fā)表于 2022-2-17 17:40 | 顯示全部樓層
man1234567 發(fā)表于 2022-2-17 17:19
有些問題看似莫名其妙的,可能是程序中有肉眼不可見的啥,也可能硬件里出現(xiàn)了個啥;
有些問題真的莫名其妙 ...

有道理,但這個問題確實(shí)想不通,平常都是遇到代碼本身邏輯問題,但第一次遇見這樣的問題,也是見鬼了。

回復(fù)

使用道具 舉報(bào)

ID:997026 發(fā)表于 2022-2-17 17:40 | 顯示全部樓層
wulin 發(fā)表于 2022-2-16 19:42
把你的確切需求和硬件環(huán)境詳細(xì)講清楚,有空給你寫一個示例。

感謝!

但應(yīng)該不是代碼本身問題,是一個莫名其妙的問題。我這個代碼連續(xù)開機(jī)測試好幾天了,挺穩(wěn)定的。
回復(fù)

使用道具 舉報(bào)

ID:236035 發(fā)表于 2022-2-18 09:36 | 顯示全部樓層
出現(xiàn)問題,能恢復(fù)到正常,則跟蹤調(diào)試恢復(fù)過程中涉及到的代碼就可以了。只要是能重現(xiàn)故障的,就一定可以找到故障點(diǎn)。至于說以前正常,突然不正常,任何項(xiàng)目都有健壯度的,不論電路還是程序。
回復(fù)

使用道具 舉報(bào)

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

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表