2020年9月21日,我突然收到一位教授的邀請,這位教授是我高中時課題研究的指導老師,他知道我的電子與計算機大概是什么水平,而他邀請我參加的正是電子設計競賽,
我去做了點功課,往年,電子設計競賽都在暑假里舉辦,今年因為特殊情況改到開學以后,教授則是學校里負責該競賽的“出口”,因為以上種種機緣巧合,我才能有這次機會,
比賽需三人組隊,這是比較難辦的,相比之下歷屆題目比較簡單,如果是我來選題的話,我所處的理科試驗班都是些次頂尖的學數學物理的人,找不到人一起參賽,只能先把密院ECE專業的女朋友拉下水,讓她再去找一個人,有幾個人表示有興趣但是沒時間,盡管現在看來“有興趣”的意思是“有興趣一起拿個獎”,我們找了一個人聊聊,我簡述了我準備比賽的計劃,對方只是點點頭,對著電腦不知看些什么,結束以后我跟女朋友商量說,要不就我們倆參賽吧,
中午我們去電院開會,除了我們組之外,別人都是人工智能大二學生,參賽可以在電子電路系統實驗課程中得高分,我是來體驗一下的,因為我一直不清楚自己的水平,以前做過幾個專案,但都是自己定的目標,現在要在別人出的題中選一道完成,我能做到嗎?我的專案都是肝出來的,從原理圖到PCB到焊接到編程一般都要一個星期,現在要在4天3夜里完成,我能做到嗎?一直都是一個人做的,現在要合作,雖然都是自己人但還是跟獨立完成不一樣的,我能做到嗎?
所以我要好好準備,仔細分析了一下2019年的題,感覺只有C題和D題是有可能做的,這類測控題好就好在只需電路,不涉及小車、無人機等機械結構,后者是我沒有接觸過的,
我備賽的宗旨“別讓比賽程序中的小事搞壞心態”,比如,去年C題和D題都要求設備能輸出一定頻率的正弦波,所以得把DAC或DDS調通;頻率高了以后ADC跟不上,就得用精密整流電路和施密特觸發器來檢測幅度和相位;這些都不應該留到比賽現場才完成,更基本地,用按鍵或旋轉編碼器選擇功能、蜂鳴器響表示測量完成、螢屏上顯示測量結果等功能都是必需的,
這樣一來就可以決定要作什么準備了:
-
再做一塊我們熟悉的開發板;
-
做一個IO模塊,放常用的輸入輸出設備,它通過I2C與開發板連接;
-
設計一系列模擬電路模塊,實作各種常用功能,如線性運算、精密整流等,
當時還在準備9月30日的數分高代考試,就沒有直接參與準備,把IO模塊的設計丟給女朋友讓她設計年輕人的第一塊PCB,要說作業也是有的,我在淘寶上下了12個單買元器件和模塊,
↑有關的、無關的PCB
十一期間只在家里待了三天半就去學校了,這三天半里也有大半都是焊接,作為老焊工,焊接的程序就省略了,在確認下載器可以檢測到單片機以后,我就帶著這些去學校了,但是且慢……
彼時我的元器件庫存已經比較成系統了,但新到的那么多快遞還是帶來了不小的壓力,最麻煩的是我要帶一些元器件和模塊到學校去,因為據說實驗室里的阻容不怎么齊全,根據我的日記描述,僅后來選題以后感覺能用上的就有:
-
挺全的色環電阻;
-
挺全的獨石電容;
-
少量電解電容;
-
2N3904、2N3906(直插不多,貼片買了,有轉接板);
-
模擬開關4051、4052、4053、4066(各2片);
-
繼電器(5個),二極管;
-
運放LMV358(5片)或LM358(20片);
-
數字電位器X9C103S(5片);
-
直插74HC595(5片);
實際帶的還有各種模塊,包括DAC、DDS等,還有一些小車能用上的,但總體來說還是把賭注往模擬電路上押的,
在學校,除了一天半的作業時間以外,別的都是寫bug與debug,所有這些bug都是有關總線的,一個I2C的,別的都是SPI的,
忘了說IO模塊的結構了,ATmega328P單片機,I2C接開發板,SPI上接74HC595、165、DAC和OLED屏,用一片138來做片選,595接4個LED和OLED的兩個控制信號,165接4個按鍵和旋轉編碼器,SPI上掛了4個設備,難免出現各種bug,
首先是595調不出來,無論怎么寫都是QH亮,把板子翻過來一看,原來是595和165焊反了,用熱風槍拆下來后焊到正確的位置,程序中還經歷了學校的焊錫不化和烙鐵頭不沾錫的問題,然后165的C引腳始終讀到0,考慮到這片165是用熱風槍拆了兩次的,就認為這個引腳壞了,于是用飛線把這個點和一個未使用的GPIO接起來了,我想過換一片165,但是找不到可以換的片,而且鋪銅上的綠皮也因為不得不開到400度的電烙鐵而脫落了不少,換個片很可能導致整塊板報廢,
用GPIO驅動165非常順利,但是改用SPI后就發生了很奇怪的現象:第1位能正常讀到,但是第2位讀到的始終與第1位相同,后面的很亂講不清楚,大體就是位與位之間有關聯,我又開始懷疑165損壞,但是換用GPIO的程式還是正常,試了幾次以后我把SPI時鐘頻率從4 MHz降到0.5 MHz,終于能正確讀取了,原來我為了不讓165的輸出與下載器的MISO沖突,給前者串聯了一個1k電阻,這使該信號的頻率上限嚴重下降,再加上那片165多少沾點nt,4MHz就這樣爆了,
某天半夜調DAC,DAC復位后應該輸出0V,但寫了正確的指令后用ADC測出來像是高阻,把10位資料的低4位都寫0,其余從零開始增長,得到分段增長的輸出電壓,我感覺是資料被移位了,果然,之前想當然地選擇SPI mode 3,而實際上該用mode 2,
OLED的RES和D/C控制信號接在了595上,SPI與單片機直接相連,跨了兩層,問題也出在這里,螢屏和接線都跟以前我做過的一個模塊一樣,只把u8g2的GPIO回呼函式改了一下,但是無法顯示,測電壓以后發現電荷泵都沒打開,依然是先懷疑螢屏壞了,但這要軟體上調不出才能下結論,我在回呼函式里加了點串口輸出,發現每次在SPI上發送一兩個位元組之前都會先設定D/C的電平,該電平需要寫595才能設定,而寫595會破壞OLED的片選信號,導致始終沒有完整的指令發送給OLED,解決方案是切斷原來595輸出到D/C的連接,改用飛線連接到另一個未使用的GPIO上,
后來螢屏成功點亮,初始化中執行了清空快取并更新現實,但是螢屏上只有前8行像素被清零,其余的很隨機,是未初始化的狀態,最后發現是隊友把初始化函式選錯了,應該選擇后綴為_f的,在記憶體中存放整個顯存,
debug的這幾天,新做的PCB也到貨了,是24個模擬電路模塊,兩個方向上都是20*4+15+1間距*4=99 mm,沒有做V割,因為出不起拼板費,我新買的小臺鋸就是用來切PCB的,只是放在了家里,我想著學校應該會有可用的設備,但跑遍學校也只有激光切割機,作業人員說沒有切過PCB,試了一下的確不行,沒辦法,只能讓家里人把臺鋸送過來,
10月8日,假期的最后一天,去實驗室熟悉了一下設備,直流電源、信號發生器和數字萬用表比較平凡,示波器得好好研究,尤其是觸發功能,那天去的實驗室里是Rohde & Schwarz的示波器,不小心碰了一下螢屏以后我們發現它竟然是觸摸屏的!總之就是非常厲害,焊了一個滯回比較器模塊,用帶噪音的正弦波作輸入,輸出沒有在輸入接近地時快速抖動,這說明實驗是成功的,在這種時候做這么簡單的實驗只是想找回一點自信心,因為此前幾天的debug程序讓我們非常擔心比賽時會繼續遇到各種各樣的bug,
↑VSSOP-10封裝,0.5 mm引腳間距
10月9日當然沒什么心情上課啦,晚上調著DAC和DDS模塊,突然群里轟來好幾個檔案,一看竟然是比賽題目!是網站方的失誤,把題目提前放了出來,組委會也決定將錯就錯,提前開始了比賽,但不提早結束,
我們開始選題,A題涉及兩個早就公布的芯片,我們查過淘寶上已經有很多方案了,可見別的組都早有準備;B題電源,沒學過;C題小車,沒學過;D題無人機,沒學過;E題模擬電路,是我們準備過的方向;F題上海不選,能選也不會;G題識別,沒學過,綜上所述,選E題,
我連夜寫了一份非正式報告,包括實作方案、附加功能、可能遇到的問題等,寫到早上5點,10日,教授說電子系的全都選了E題,我切身體會到了什么叫內卷,
但是換題已經沒有可能了,我們只好卷下去,我們先去另一間實驗室熟悉環境——我們將在那里待上4天3夜,開了一臺示波器,從Windows的logo就開始不對勁,開機以后發現是花屏了,又開了一臺,也花屏,我開始懷疑這間實驗室是廢舊儀器倉庫,后來教授帶著終于湊齊了一套好用的電源、信號發生器、示波器和萬用表,
審題放在下一節講,方案離不開共射放大電路,我開始用Multisim來調引數,無失真、頂部失真、交越失真都沒有問題,雙向失真暫時沒有思路,而在底部失真的仿真中我發現了很奇怪的現象:底部失真本應是輸出波形的底部被削平,但仿真結果卻是底部波形上翻,用面包板搭建電路后也是如此,問教授后得知一般發射極都要加電容,在保持直流作業點的同時提高放大倍數,這下雙向失真也很容易實作了,至于不加電容時波形會上翻,是因為集電極電壓不能低于發射極,而放大倍數不很大時基極電壓的變化就會反映在輸出上,
隊友用面包板搭建了一個兩級負反饋三極管放大電路,本來打算用作前級小信號放大的,搭面包板的程序中,我們發現還是自己帶的電阻電容比較齊全,
晚上,隊友寫了個C++程式試試FFT和THD,把FFT采樣頻率改到與正弦波頻率的幾倍差一點,發現THD比較小,這說明THD的測量對采樣頻率的精度要求不高,
說說別的組,我一早上就去實驗室了,其他3組到中午才有人來,有一組的另兩個人晚上才來,這才完成了選題,此時,我們已經有了方案,就等第二天開始制作了,
↑模擬電路模塊
我一開始的想法是設計5個放大電路,用模擬開關來切換通道,教授說可以通過改變放大倍數實作,我覺得這個想法也不錯,但是仔細思考又感覺不對:放大倍數小的時候是正弦波,大了以后要么頂部失真,要么底部失真,再大變成雙向失真,所以為了同時獲得頂部和底部失真需要調整直流作業點;而且這種方法不能涵蓋交越失真,這些意味著需要好多級的切換,雖然各種模擬開關我都有,但是會讓設計的層次不那么分明,比較復雜,最侄訓是采用了原來的方案,
在內卷的競爭中,我們必須在附加功能上花工夫才能脫穎而出,所以除了題目要求的功能以外我們還打算加入波形顯示和聲音輸出的功能,
設備共由4塊板組成:一塊洞洞板對外接12 V電源、輸入和輸出,板上有LDO、模擬開關和三極管放大電路,以及與其他板連接的引腳,直流作業點都可以用電位器來調整;一塊洞洞板上焊接兩個模塊并粘了一個揚聲器,負責信號的運算,即把12 V范圍內的信號耦合到5 V以內;一塊開發板;一個IO模塊作UI,
演算法部分,FFT選用了Arduino FHT庫,以1 kHz的32倍頻采樣,FFT規模256,計算16位線性幅度,然后根據題目里的公式取相應的FFT結果來計算THD——fht_lin_out[0]是直流分量的幅度,[8]是基頻,[16]是兩倍頻等,一開始是16倍采樣頻率,規模128,為了精度和頻率解析度再加一倍,但是效果不太理想,最后只能夠用就行了,
在25 MHz主頻下,1 kHz的32倍頻對應781.25個周期,盡管無法讓ADC采樣精確地發生在分數周期時刻,但也要避免0.25周期累加產生的影響,為此,以4次為周期,OCR1A暫存器的值取1次781、3次780,后來發現這點精度損失完全可以忽略不計,
IO模塊的按鍵是事件驅動的,按下時IO模塊會通過I2C通知開發板,程式切換一個波形后,需要先等待1秒讓直流作業點穩定,再以0.5 s為周期執行采集、計算、顯示等操作,只有空閑的時候才允許退出,盡管定義過事件啟用禁用的指令,但是一是怕出現一些破壞原子性的情況,二是異步終止一個程序的流程不好寫,所以我又把異步事件改回同步——在回呼函式里設定標志位,在流程里需要的時刻輪詢,
THD計算中需要開根號,FFT到頻譜圖需要取對數,都可以用二分演算法來實作,開根號的程序是對結果進行二分查找,一個小細節是AVR開發中int默認是16位,uint16_t乘uint16_t會溢位,要先轉換為uint32_t,取對數是把0-10000映射到0-48,先在Excel中生成指數表,作為陣列放行程式,寫一個類似std::lower_bound的二分查找演算法獲取index,即為結果,
示波器穩定影像的方法是使用觸發功能,基于這種想法我們在256長的資料中找出第一個在512±16滯回比較器中的上升沿,作為2波形周期64項的開始,后來有了硬體滯回比較器,但還是沿用了軟比較器作觸發,
最后一天還想加點功能,教授說題目里沒有明確說明要測THD的都是1 kHz的信號,用FFT直接計算基頻是不準的,因為頻率解析度只有31.25 Hz,但FFT能給出基頻的范圍,即第一個峰值附近,我定義為比左右兩邊都大且超過最大值的一半的第一個值,找出這個index,把它右邊一格對應的周期作為周期的下限,然后用連接到硬體滯回比較器的GPIO檢測上升沿,累加上升沿間隔直到超過下限,記錄下經過的周期數,測量16次,排序,取中間8個的平均值作為信號的周期,然后32倍頻ADC采樣,后續流程相同,
↑滯回比較器的輸入與輸出波形(先用運放湊合一下)
11日開始制作設備,上一次焊洞洞板還是剛接觸PCB的時候,那時做了一個電位器調音量的電路,最后失敗了,所以我對洞洞板一直不太看好,這次畫PCB已經來不及了,只好硬著頭皮上洞洞板了,
洞洞板上共有6個放大電路:前級放大和5種波形對應的電路,每個子電路和電源都連接到排針作輸入輸出,兩級之間暫時用杜邦線連接,先焊好前級,測驗一下沒問題,心里就有底了,一下午焊完了6個電路,調好電位器后都能正常作業,
反面的接線挺亂的,盡管元器件是按照信號方向從左到右、電壓高低從上到下的規律排列的,典型的如GND分布在每個子電路的多個電阻上,我直接用電阻的引腳拉到旁邊的GND上,每個子電路匯聚到一個點再接到最下方的一長條焊錫上,有些短程的連接要跨線,我用剪下的引腳或飛線跨接,
模擬開關進入戰局后情況就變得不太樂觀了,首先是我不知為何要用兩片CD4051,其實只要輸出端一片就夠了,這幾乎把接線的復雜程度翻了一倍,模擬開關作業在12V,單片機輸出信號需要轉換才能用,我就在洞洞板背面用貼片三極管和貼片電阻焊了3個反相器,輸入接排針,輸出用飛線接到兩片4051上,這一步很困難,本來就是三極管集電極和貼片電阻之間一點點地方,還要接到兩個引腳上,還那么靠近,為了測驗方便,在排針上面還加了一排高電平,這樣用跳線就可以選擇通道了,然后又把LDO、輸入輸出耦合等加進來,一步步地很費時間,做完已經是第二天凌晨了,
我們需要為4塊板找一塊底板固定起來,實驗室找不到新的大洞洞板,只能用一塊用過一點的大板,把用過的部分切下來,然后用臺鉆鉆3 mm孔,用銅柱和螺絲固定上去,板上的電源和輸入輸出用的是香蕉和BNC插座,為了焊到板上也是要先鉆孔,BNC插座的外層不沾錫,我拿從杜邦線中拆出的銅絲繞引腳一圈焊上,才使連接穩定,
最后我們聽取教授的建議,為了穩定把杜邦線連接改為焊接,我的隊友完成了這項作業,第三天晚上完成這些,然后一起把報告寫了,隊友負責軟體部分,我負責其余全部,最后一天做的就沒有寫進報告里了,
↑成品
實際程序當然沒有這么順利——又到了喜聞樂見的debug環節,先講硬體問題,
虛焊出現過兩次,用探頭測波形,要按下才會有波形顯示,開始幾次稍微用力即可,后來要按到底才行,從側面看,按到底的時候焊點已經碰到了桌面,所以我推測是焊接問題,把用轉接板和排針連接的三極管換成直插的,解決了問題,此后三極管只敢用直插的了,還有一次是用錫走線沒有連接完全造成的,
輸出失真波形的平直段傾斜,測了各級的輸出后發現是最后一級耦合到地的問題,把105獨石電容換成10 μF電解電容解決了問題,
功放芯片只找到LM386,隊友按照datasheet上的電路焊了洞洞板,但揚聲器里的聲音是一定頻率的爆音,且爆音頻率與輸入電壓正相關,想到可能是輸入耦合的問題,加了電容但是沒有改善,我又用面包板搭了同樣的電路,換了多個芯片,都是同樣的問題,此時她正因為前兩天沒能幫上什么忙而難過,這下電路沒做成更加沮喪,而我也一下子沒想到什么可行的方案,但我深知不能兩個人一起頹廢,情急之下我想到了之前做的模塊(還記得準備階段的目標嗎?),正好有一個用三極管擴大運放輸出電流的模塊可以用作功放,板子早就切好了,焊上電阻電容二三極管,外部只需再加一個電解電容,揚聲器就能響了,
12日晚,臨寫報告時又讀了一遍題,發現忘了題中的K1和K2兩個SPDT,實驗室一時半會也找不到開關,我急中生智把開發板上兩個用不到的撥動開關拆了下來,焊在洞洞板上,再小改了一下接線,
然后是軟體問題,動手寫代碼之前我就意識到了異步事件的問題,我的隊友是搞不定的,所以我就安排她把ADC->FFT->THD的程序寫成一個函式,不用管函式呼叫的位置以及前后需要做什么,但是吧,問題就在于機械革命到了Ryzen這一代品控還是繼續差,隊友的筆記本掉螢屏了!還好掉得早,已完成的只占一小部分,后面的代碼就都是我在我用了近三年的筆記本上寫的了,這是電腦的硬體問題,開發程序中的軟體問題,
第一次跑半完整程式的時候,啟動時間遠長于預期,至少到了秒的數量級,而正常的啟動時間應該不到100 ms,在代碼一開始加上翻轉引腳電平的指令,接邏輯分析儀發現啟動程序有三次復位,并且都是硬體復位,我把初始化程序中呼叫的函式一個個注釋掉,最后發現是ADC的初始化導致了復位,進一步分析,可能的原因是開啟ADC時AREF上的0.1 μF電容會被充電,使5 V電壓突然下降,降到了brown-out電壓以下,觸發單片機復位,我把brown-out電壓從4.3 V改到2.7 V,單片機就不再復位了,
用邏輯分析儀看波形時發現I2C指令之間有10 ms量級長的間隔,兩邊用的I2C驅動是我自己寫的異步寫同步讀,只有在緩沖區滿時才會阻塞,從機端的處理也全部在中斷中進行,仔細觀察波形后我發現是從機在地址位元組之后把SCL拉低了,阻止主機發送后續資料,而這是因為程式沒有及時進入相應的中斷去寫暫存器,實際上程式還在處理上一個繪圖指令,繪圖是比較耗時的,我想到可以用類似的方法把指令放在緩沖區中,在定時器中斷中取指令并執行,后來發現效果不怎么理想,一是間隔仍然存在,二是緩沖區受不了128根柱子的資料量,造成資料丟失,所以又換回來了,
Arduino FHT輸入為16位帶符號數,ADC只能讀到10位,這樣輸出的幅度很小,精度不高,把ADC讀到的值左移6位再在低位補上高6位作為輸入,卻得不到正確的FFT輸出和THD值,檢查后發現忘記考慮符號了,只需把MSB翻轉即可,
開發板處多次出現記憶體不足的情況,一開始FFT規模為128,OLED緩沖區占1KB,要顯示的字串太多,占用了很多記憶體,我就把它們移到flash中去,留出了一百多位元組的空余記憶體,后來FFT規模改到256,記憶體怎么都省不出來了,只能改OLED的顯示方式,只給它分配256位元組顯存,每幅影像分4次繪制,
以上bug其實占用了有效比賽時間的大部分,所以我其實是沒什么時間睡覺的,每天回一次寢室拿東西,晚上睡在實驗室樓下大廳的沙發上,有一天打算1點睡,結果debug到4點才睡;又有一天打算4點睡,隊友開玩笑說我怕是要7點睡了,結果我真的7點準時叫她起床了,
↑10月4日,外灘燈光秀
比賽結束后我好好睡了一晚上,第二天睡了一上午、一中午、一下午和一晚上,
15日校測,我們帶著箱子到陌生的實驗室測驗,我們復原后測驗時,發現雙向失真的輸出波形達不到2 Vpp了,調電位器也沒有用,我想起制作時因為雙向失真輸出峰峰值明顯高于其他幾個波形,就把下端的分壓電阻并聯上一個相同阻值的,現在只要把這個額外的電阻取下來就可以了,不用動電烙鐵,直接用鉗子剪斷一端就好了,
我們很快就準備好了,開始關注其他組的情況,盡管不能看,有一組沒有加K1和K2,從而有專案無法測驗,我之前注意到這點的時候思考過這兩個SPDT有什么用,思考的結果是為了驗證THD是真實的,即給一個外來的波形要求測THD,否則THD是可以用現成設備測好后硬編碼行程式的,盡管我在實驗室沒有找到過可以測THD的設備,大三的幾組都做了很大的板,板上甚至有三極管的陣列,以至于我以為他們是選電源題的,后來聽說這是他們為了大作業做的,
我們的測驗很順利,并且兩個開關的功能正如我所預料的,題目要求能測量1 kHz、2 Vpp的方波和三角波的THD,然后與標準答案比對,我們的設備測量得有一點誤差,但在合理的誤差范圍內,
到了18日位于隔壁華師大的市測,測驗的專案大致相同,波形換成了理論THD為8%和20%的,頻率仍然是1 kHz,進入這個模式前專家先告訴我,設備顯示的THD要能在切換波形時自動更新,這是一個得分點,專家讓我調設備進入測量模式,此時還沒有信號源,因此我以設備需要先測量信號頻率為由拒絕了,并要求先開啟信號源,專家同意了,因為要自動更新,所以得在螢屏上THD的瞬時值和平均值中選擇前者來讀,第一個是8.5%,第二個20.5%,上下波動均不超過0.2%,我很滿意,
最后得了一等獎,但此時這已經是次要的了,我很累,聽說電賽不能找室友組隊,因為室友以后還要一起住;而我選擇了最親近的人一起參賽,可以想象我們經歷了多少困難,如果還有機會,我不想再參賽了,
回想起來,比賽的幾天我沒有過任何重大技術突破,所有技術要點都是炒冷飯,那么時間都用哪去了呢?大概是以上十幾個bug吧,我不想以后回憶起這段參賽經歷只想得起我睡得多少、有多累,而是經驗——那些踩過的坑、避過的雷、de過的bug,故為此文,
那些借用來的技術要點以及它們的來源包括:
-
I2C通信、整個流程控制框架,來自我高中時的課題,本博客沒有;
-
三極管放大電路、FFT,自制藍牙音箱的手冊;
-
示波器,AVR單片機教程——示波器;
-
二分演算法,來自遙遠的C++、DSA記憶;
-
硬體、軟體的debug經驗,分布于我的開發經歷中;
-
等等……
我愿稱之為“縫合怪”,
↑自己做的電賽限定版PCB鑰匙扣
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/194440.html
標籤:其他
