找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

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

關(guān)于STM32嵌入式程序調(diào)試的體會(huì)

  [復(fù)制鏈接]
ID:599678 發(fā)表于 2020-3-30 11:21 | 顯示全部樓層 |閱讀模式
工作5年,野路子。由感而發(fā),隨便寫(xiě)點(diǎn),混點(diǎn)積分。
工作之后已經(jīng)很久沒(méi)有在論壇上活躍了,一方面沒(méi)有時(shí)間,另一方面有問(wèn)題時(shí)直接聯(lián)系廠家技術(shù)支持了,很少在論壇里面請(qǐng)教?hào)|西。近來(lái)比較空閑,分享下自己寫(xiě)的一些代碼方法。相信很多人初學(xué) STM32是參考原子哥的例程的,為了方便大家理解,以下所寫(xiě)就在原子哥基礎(chǔ)上加上一些自己的東西,當(dāng)然很多也不定是我的東西,只不過(guò)掌握了而已。

就先說(shuō),這個(gè)大家想必大家都會(huì)。就直接開(kāi)門(mén)見(jiàn)山了,說(shuō)下下面兩點(diǎn),串口發(fā)送和封 裝串口功能。對(duì)于串口發(fā)送,下面是原先的:

這種串口發(fā)送在波特率不是很高時(shí)不推薦這樣寫(xiě),就以波特率 9600 來(lái)算,發(fā)送 1 位就需要
104us,發(fā)送一字節(jié)(8 位數(shù)據(jù)+1 位起始位+1 位停止位)就需要 1ms,可以看到在發(fā)送的 時(shí)候 MCU 是處于一直等待的狀態(tài),如果發(fā)送 100 個(gè)字節(jié),就在這里等待 100ms 以上,在項(xiàng) 目中使用顯然不合適。

實(shí)際在用的過(guò)程中,是建立一個(gè)狀態(tài)標(biāo)志,用來(lái)表示串口發(fā)送是忙還是空閑,當(dāng)我們發(fā)送置 為忙,發(fā)送完成置為空閑。利用串口發(fā)送中斷避免原子例程中的while 等待。如下面:


對(duì)于沒(méi)有使用過(guò)串口發(fā)送中斷的,可以自行搜索,這里不做過(guò)多解釋?zhuān)傊@種串口發(fā)送不 會(huì)讓主程序一直等待硬件標(biāo)志,當(dāng)然這種發(fā)送方式需要上層應(yīng)用做出判斷,就是在當(dāng)前數(shù)據(jù) 沒(méi)有發(fā)送完前,不能發(fā)送新的數(shù)據(jù)。原先的寫(xiě)法可以連續(xù)發(fā)送很多條字符串,現(xiàn)在需要寫(xiě)個(gè)

狀態(tài)機(jī),判斷當(dāng)前串口是否忙,然后才能進(jìn)行下一步操作。例如有 10 個(gè)數(shù)組,把 10 個(gè)數(shù)組 分別通過(guò)串口發(fā)送出去,每個(gè)數(shù)組發(fā)送完需要間隔 10ms 才能發(fā)送下一個(gè)數(shù)組。要實(shí)現(xiàn)這樣
的功能,當(dāng)然可以用一個(gè) for 循環(huán)搞定這個(gè)操作,但是如果在不影響實(shí)時(shí)性的基礎(chǔ)上實(shí)現(xiàn), 這個(gè)會(huì)在后面的程序框架中講到。

串口發(fā)送就到此為止,接下來(lái)講下封裝串口功能。對(duì)于一個(gè)多人維護(hù)的項(xiàng)目來(lái)說(shuō),并不需要 每個(gè)人都把全部代碼或者別人寫(xiě)的代碼都一一摸清楚,一個(gè)串口,我需要的就是很簡(jiǎn)單的功 能,打開(kāi)串口,串口來(lái)數(shù)據(jù)里怎么辦,串口發(fā)送完數(shù)據(jù)了怎么辦。就像下面這種:

這是一個(gè)函數(shù)申明,前三個(gè)參數(shù)是需要打開(kāi)的串口號(hào)、波特率、校驗(yàn),第四和第五個(gè)參數(shù)是 函數(shù)指針,函數(shù)指針就是一個(gè)指針,只不過(guò)指向的是函數(shù)而已。這里來(lái)說(shuō)明一下函數(shù)指針的 用法:
可以看到,函數(shù)指針可以指向一個(gè)同類(lèi)型的函數(shù),并且可以運(yùn)行這個(gè)函數(shù)指針指向的函數(shù)。 知道這些接下來(lái)就可以亮出代碼解釋函數(shù)申明里面的兩個(gè)回調(diào)函數(shù)了。

先從底層說(shuō)起,首先申明一個(gè)結(jié)構(gòu)體類(lèi)型,

然后定義這個(gè)結(jié)構(gòu)體變量,這個(gè)結(jié)構(gòu)體分別對(duì)應(yīng) 5 個(gè)串口的基本信息:

以串口 1 的中斷函數(shù)為例,通過(guò)紅色框中可以看出,每接收一個(gè)串口數(shù)據(jù),會(huì)調(diào)用對(duì)應(yīng)結(jié)構(gòu) 體變量的 rx_call_back 函數(shù)指針。每發(fā)送完串口數(shù)據(jù)會(huì)調(diào)用結(jié)構(gòu)體變量的 tx_call_back 函數(shù)
指針。

可以看到,這兩個(gè)函數(shù)指針指向的函數(shù)已經(jīng)被執(zhí)行,就差一點(diǎn)了,這個(gè)函數(shù)指針指向的是哪 個(gè)函數(shù)?好了回到串口初始化函數(shù),如下:


可以看到所指向的函數(shù)是通過(guò) uart_open 傳遞過(guò)來(lái)的,具體指向哪個(gè)函數(shù)交于上層調(diào)用
uart_open 的來(lái)指定,底層的工作先到此結(jié)束(為了使代碼好看,把串口 2、3、4、5 及引腳 配置都省略了,寫(xiě)這篇的意義不在意代碼,而是思路)。

底層再補(bǔ)充一下串口發(fā)送及狀態(tài)獲取的代碼吧:


這下底層的工作真的到此結(jié)束了。再?gòu)纳蠈咏嵌瓤聪略趺词褂眠@個(gè)函數(shù),比如通過(guò)串口 1 和

NB 模組進(jìn)行通信,下面就是一個(gè)打開(kāi)串口的函數(shù):


下面是串口回調(diào)函數(shù):

可以看到上層程序設(shè)計(jì)相對(duì)來(lái)說(shuō)已經(jīng)不再過(guò)多涉及 STM32 本身的操作了,這種回調(diào)函數(shù)的 用法,使得程序分層設(shè)計(jì),思路上更加清晰,方便維護(hù)。

再說(shuō)時(shí),也是開(kāi)門(mén)見(jiàn)山,直接說(shuō)自己的兩點(diǎn),封裝定時(shí)器和如果使用定時(shí)器。 先說(shuō)如何封裝定時(shí)器吧,其實(shí)也就是回調(diào)函數(shù),和上面封裝串口一樣。這里就直接曬代碼了,
下面是底層代碼:



下面是上層代碼,和封裝串口類(lèi)似,上層只要知道打開(kāi)一個(gè)定時(shí)器,多長(zhǎng)時(shí)間進(jìn)入自己定義 的定時(shí)器中斷就可以了。

接下來(lái)就是如何使用定時(shí)器了,我這定時(shí)器可能和你想的不一樣,這里主要介紹一下思路。 前面都是先從底層說(shuō)起,這里換個(gè)角度先從上層說(shuō)起,對(duì)應(yīng)上層的設(shè)計(jì)人員,該人員需要的

僅僅是定義一個(gè)變量,然后注冊(cè)到定時(shí)器里。如下面:


當(dāng)上層人員調(diào)用 time_cnt_reg 函數(shù),實(shí)際就已經(jīng)注冊(cè)一個(gè)定時(shí)器了,比如 rx_over_time 就會(huì) 每 1ms 增加 1,inq_reg_over_time 每 1s 增加 1。上層人員知道這么用就可以了,當(dāng)然也許 你會(huì)好奇,對(duì)這個(gè)變量進(jìn)行查找,并沒(méi)有對(duì)該變量的操作,怎么就增加了呢。

從接下來(lái)開(kāi)始,所說(shuō)的操作也不算是底層操作了,應(yīng)算是整個(gè)程序設(shè)計(jì)中的框架功能。以下 就從框架功能角度說(shuō)了,主要就是 time_cnt_reg 是怎么一回事。 其實(shí)也很簡(jiǎn)單,定義一個(gè)鏈表,鏈表中就一個(gè)時(shí)間單位,是 ms 還是 s,然后就是一個(gè)指針, 指向的是待注冊(cè)的變量。



每次調(diào)用 time_cnt_reg 函數(shù),就會(huì)向鏈表中增加一個(gè)成員,成員中的 cnt_ptr 指向 time_cnt_reg中帶入?yún)?shù)地址。

下面就是對(duì)鏈表中的成員進(jìn)行自加 1 操作,

上面這兩個(gè)函數(shù)在哪里使用的呢,回到封裝定時(shí)器操作,里面的回調(diào)函數(shù)做了什么?

這下應(yīng)該清楚了注冊(cè)定時(shí)器是怎么一回事了。我這里用的注冊(cè)定時(shí)器用的鏈表方式,也可以 改成結(jié)構(gòu)體數(shù)組。以前我也用過(guò)結(jié)構(gòu)體數(shù)組,結(jié)果有次注冊(cè)的定時(shí)器超過(guò)定義的上限,結(jié)果 排查了半天才找到原因才改的使用鏈表。

好了,再回到上層應(yīng)用,注冊(cè)好定時(shí)器后,應(yīng)用起來(lái)就和正常使用的一樣就可以了,可能你 會(huì)說(shuō)既然用起來(lái)都一樣,為何你這還要多此一舉,這個(gè)變量完全可以在定時(shí)器中斷里面直接 使用 rx_over_time++搞定的事么。這個(gè)么仁者見(jiàn)仁智者見(jiàn)智,有興趣的可以接著往下看。


再說(shuō),很多人喜歡上一些實(shí)時(shí)操作系統(tǒng),在我看來(lái)其實(shí)沒(méi)有必要,那些實(shí)時(shí)操作 系統(tǒng)可以當(dāng)做學(xué)習(xí) LINUX 的過(guò)渡,但使用起來(lái)沒(méi)覺(jué)得哪里特別的。自己搭一個(gè)抽象系統(tǒng)+狀
態(tài)機(jī)就可以搞定的事情,程序設(shè)計(jì)更加健壯、可控、高效。這里的抽象系統(tǒng)就算是我所說(shuō)的 程序框架吧。

先說(shuō)務(wù),通過(guò) app_reg 注冊(cè)一個(gè)任務(wù),思路和前面使用定時(shí)器差不多,每次 app_reg 執(zhí)行都會(huì)向鏈表中添加一個(gè)節(jié)點(diǎn),然后在定時(shí)器回調(diào)函數(shù)中對(duì)該APP 時(shí)間計(jì)數(shù)加 1 操作。 為了節(jié)約篇幅這里就不貼出這部分代碼了。

主函數(shù)的代碼很短,主要就是 app_init 和 app_manage,查看源碼發(fā)現(xiàn)僅僅有 2 處區(qū)別, app_init 函數(shù)不需要對(duì)應(yīng)的計(jì)數(shù)器到達(dá)設(shè)計(jì)值就可以,但是app_manage 需要計(jì)數(shù)器到達(dá)設(shè) 定值才執(zhí)行。其次 app_init 執(zhí)行時(shí)帶入的參數(shù)為 0,app_manage 執(zhí)行時(shí)帶入的參數(shù)為 1。



下面以?xún)?nèi)部看門(mén)狗的示例展示一下用法:


可以從代碼中看出,data 為 0 時(shí)即初始化看門(mén)狗,為 1 時(shí)即每 50ms 執(zhí)行一次,我個(gè)人覺(jué)得 這么寫(xiě)的話(huà)代碼緊湊一些,方便查看。看過(guò)很多裸跑的程序,建立幾個(gè)時(shí)間標(biāo)志,10ms 到 了干什么什么事,1s 到了干什么什么事,結(jié)果同樣是一個(gè)事情,這里一行代碼,那里一行代 碼,時(shí)間長(zhǎng)了就不知道還有哪里會(huì)有這些代碼。

再以一個(gè)例子引入下一個(gè)程序功能吧,485 總線(xiàn)收發(fā)數(shù)據(jù),當(dāng)需要發(fā)送 485 數(shù)據(jù)時(shí),先將控 制收發(fā)引腳置為發(fā)送,然后將串口數(shù)據(jù)發(fā)送出去,然后稍微加些延時(shí),再然后將控制引腳置 為接收。我想很多人操作這個(gè)時(shí)應(yīng)該是讓 MCU 硬等待在這一塊?戳宋疑厦鎸(xiě)的串口代碼, 可以這樣寫(xiě),當(dāng)發(fā)送時(shí),先將 485 置為發(fā)送,然后發(fā)送數(shù)據(jù),在串口發(fā)送完成回調(diào)函數(shù)中將
485 再置為接收。 然后實(shí)際測(cè)試卻不是這樣,發(fā)送串口數(shù)據(jù)完成然后在回調(diào)函數(shù)中立刻置為接收,會(huì)造成 485 最后一個(gè)字節(jié)數(shù)據(jù)發(fā)送不完整,在回調(diào)函數(shù)中加入延時(shí)再置為接收就沒(méi)有問(wèn)題,但是這里的 延時(shí),實(shí)際上就是在串口發(fā)送中斷函數(shù)執(zhí)行的,很明顯不合理。如何解決這個(gè)問(wèn)題,程序框 架中又加入一項(xiàng)功能:長(zhǎng)時(shí)執(zhí)個(gè)數(shù)執(zhí)。具體設(shè)計(jì)思路不再貼出 來(lái)了,實(shí)現(xiàn)這一功能和前面的都差不多,這里只說(shuō)下怎么應(yīng)用的,con_485_recv 是一個(gè)控制
485 為接收的函數(shù),在串口發(fā)送完成回調(diào)函數(shù)中執(zhí)行 fun_once_ms_late_reg(10, con_485_recv);

即 10ms 后執(zhí)行 con_485_recv。為了避免 485 連續(xù)發(fā)送幾條數(shù)據(jù)時(shí),之前的 con_485_recv 到 時(shí)間了執(zhí)行,在發(fā)送數(shù)據(jù)時(shí)除了置為發(fā)送,還要調(diào)用fun_once_ms_late_unreg(con_485_recv),
即取消還有多長(zhǎng)時(shí)間后執(zhí)行的 con_485_recv,避免時(shí)序錯(cuò)誤。

再說(shuō)態(tài)機(jī),以操作 NB-IOT 工作在主動(dòng)上報(bào)類(lèi)為例,相信很多人看過(guò)原子的操作 AT
指令的函數(shù),如下:


然后就是調(diào)用這個(gè)函數(shù)一個(gè)個(gè)執(zhí)行 AT 命令,這種寫(xiě)法在例程里作為實(shí)驗(yàn)是可以的,但是實(shí) 際應(yīng)用不該這么寫(xiě),影響系統(tǒng)的實(shí)時(shí)性,也難以維護(hù)。下面是操作NB 模組工作的流程圖, 按照如下流程進(jìn)行程序設(shè)計(jì)。



下面是對(duì) NB 狀態(tài)的相關(guān)定義、變量申明及初始化為上電流程

下面就是主要的處理了,我們讓 nbiot_proc 運(yùn)行的時(shí)基為 50ms,AT 命令發(fā)送處理中,不同 AT 指令響應(yīng)的時(shí)間不同,有些需要幾百毫秒,有些需要幾秒,就通過(guò) nb_work.delay 實(shí)現(xiàn)發(fā) 送處理的延時(shí)處理,這里的延時(shí)并不影響系統(tǒng)的實(shí)時(shí)性。另外對(duì)比發(fā)送處理和接收處理,可

以發(fā)送不同的流程下每個(gè)流程都公用一個(gè)處理函數(shù),只不過(guò)帶入的參數(shù)不同,我覺(jué)得這樣寫(xiě) 便于閱讀而已。



以上電初始化代碼參考如下,可以看到,首先斷電并等待 2s,然后上電等待 5s,然后置為
參數(shù)初始化流程,在這 5s 期間,如果收到模塊發(fā)出的對(duì)應(yīng)信息,提前結(jié)束等待時(shí)間,并且 轉(zhuǎn)到下一個(gè)流程。其實(shí)通過(guò)這一個(gè)流程就已經(jīng)差不多表述了我所想表達(dá)的狀態(tài)機(jī)寫(xiě)法,這樣 的寫(xiě)法有兩個(gè)好處,一是實(shí)時(shí)性保持良好,二是通過(guò) state 和 process 就可以知道當(dāng)前模塊 工作在什么狀態(tài)哪個(gè)流程,做到“可控”。


下面是對(duì)接收到的消息處理,主要就是把 NB 收到的消息放入消息隊(duì)列里(稍微有些刪減):


下面是發(fā)送消息的處理,其實(shí)就是申請(qǐng)一個(gè)消息隊(duì)列 上層只管對(duì)消息隊(duì)列進(jìn)行處理,而消息隊(duì)列的處理,是通過(guò)下面的這個(gè)函數(shù)進(jìn)行處理。其實(shí)
這個(gè)并不是隊(duì)列,只是一種延伸的用法,不過(guò)習(xí)慣了這么叫。


再說(shuō)下一些微小功能吧,主要是方便而已。調(diào)試,這個(gè)可以用來(lái)和 MCU 進(jìn)行一
些 SHELL 指令交互,比如板上有個(gè) NANDFLASH 或者 SD 卡,搭載文件系統(tǒng),想要看里面文 件時(shí),總不能每次都把卡拔出來(lái)看吧,這時(shí)可以通過(guò)調(diào)試串口進(jìn)行命令交互,就像下面一樣, 當(dāng)然這部分程序要寫(xiě):

當(dāng)然還可以有其他功能,比如查看任何一個(gè)外設(shè)的狀態(tài),查看某個(gè)串口交互的全部報(bào)文等,

就看應(yīng)用者如何賦予功能?偛荒芟肟聪麓谑瞻l(fā)報(bào)文,用個(gè) USB 轉(zhuǎn) TTL 焊在對(duì)應(yīng)的引腳 上吧,太不方便了。



說(shuō)LOG,批量的東西,經(jīng)常有很多難以復(fù)現(xiàn)的問(wèn)題,這些問(wèn)題很多是在特定的情況 下觸發(fā)的軟件 BUG,這種情況下,如果有 LOG 功能,就方便分析問(wèn)題了,但是如果沒(méi)有那 也只能靠猜了。導(dǎo)出 LOG 也可以通過(guò)調(diào)試串口來(lái)實(shí)現(xiàn),總之就是為了方便。


再說(shuō),其實(shí)這一塊感覺(jué)也沒(méi)啥寫(xiě)的,設(shè)計(jì)思路是這樣的,對(duì)于任何一個(gè)模塊,比 如 NB 模塊和對(duì) NB 消息隊(duì)列的處理,每個(gè)都向睡眠機(jī)制中注冊(cè)一個(gè)變量,NB 底層狀態(tài)機(jī) 處理只要不是空閑,對(duì)應(yīng)變量都為真,NB 消息隊(duì)列只要有未處理的數(shù)據(jù),對(duì)應(yīng)變量也為真。 睡眠管理在 while(1)里,檢查所有注冊(cè)的變量,當(dāng)所有的變量都為假時(shí),調(diào)用注冊(cè)的回調(diào)函 數(shù)并進(jìn)入睡眠,然后多長(zhǎng)時(shí)間喚醒一次再通過(guò)另一個(gè)回調(diào)函數(shù)進(jìn)行某項(xiàng)處理。下面是應(yīng)用在 華大一款低功耗芯片的 main 函數(shù):


對(duì)應(yīng)的低功耗還有時(shí)鐘切換,也是向時(shí)鐘管理機(jī)制中注冊(cè)一個(gè)變量,比如某段時(shí)間只有幾個(gè)
led 亮著,那就用 32768Hz 的時(shí)鐘做主頻,如果有用 IIC 的,就用 4M 做主頻等等,while(1) 都是對(duì)注冊(cè)的變量進(jìn)行判斷管理。 其實(shí)之前也沒(méi)有搞過(guò)太多低功耗的東西,只是不想破壞原有的各種外設(shè)的代碼及風(fēng)格,便這 樣處理了。下面是睡眠管理處理,涉及到對(duì)底層寄存器的處理和芯片的工作模式,看過(guò)一段 時(shí)間現(xiàn)在忘了。時(shí)間長(zhǎng)了我都不知道寫(xiě)的啥,只知道上層怎么使用的了,但這我覺(jué)得就夠了。



先寫(xiě)到這邊吧,看不明白沒(méi)關(guān)系,因?yàn)槲矣X(jué)得自己寫(xiě)的都不知道寫(xiě)的啥,文筆太差,隨便寫(xiě)寫(xiě)。

以上的pdf格式文檔51黑下載地址(內(nèi)含清晰圖):
嵌入式軟件隨筆.pdf (1.37 MB, 下載次數(shù): 36)

評(píng)分

參與人數(shù) 2黑幣 +65 收起 理由
IdeaMing + 15 很給力!
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

回復(fù)

使用道具 舉報(bào)

ID:599678 發(fā)表于 2020-4-1 08:46 | 顯示全部樓層
已上傳的文檔好像不能再次編輯,可以通過(guò)GIT獲取最新WORD版文檔,不定期更新。有任何疑問(wèn)此貼必回。
https://gitee.com/hubaojin/ARM_DEBUG_STUDY.git

評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 回帖助人的獎(jiǎng)勵(lì)!

查看全部評(píng)分

回復(fù)

使用道具 舉報(bào)

ID:722501 發(fā)表于 2020-4-4 21:07 | 顯示全部樓層
感謝分享
回復(fù)

使用道具 舉報(bào)

ID:722501 發(fā)表于 2020-4-4 21:07 | 顯示全部樓層
感謝分享
回復(fù)

使用道具 舉報(bào)

ID:405033 發(fā)表于 2020-4-4 22:13 | 顯示全部樓層
受教頗多,謝謝分享經(jīng)驗(yàn)
回復(fù)

使用道具 舉報(bào)

ID:265975 發(fā)表于 2020-4-4 22:21 | 顯示全部樓層
支持樓主
回復(fù)

使用道具 舉報(bào)

ID:265975 發(fā)表于 2020-4-4 22:22 | 顯示全部樓層
受教頗多,謝謝分享經(jīng)驗(yàn)
回復(fù)

使用道具 舉報(bào)

ID:721835 發(fā)表于 2020-4-5 17:48 | 顯示全部樓層
感謝分享啊~學(xué)習(xí)了~
回復(fù)

使用道具 舉報(bào)

ID:128463 發(fā)表于 2020-4-11 10:15 | 顯示全部樓層

感謝分享。
回復(fù)

使用道具 舉報(bào)

ID:691681 發(fā)表于 2020-4-12 19:59 | 顯示全部樓層
調(diào)理清楚,表達(dá)清晰準(zhǔn)確!樓主加油!
回復(fù)

使用道具 舉報(bào)

ID:399788 發(fā)表于 2020-4-25 01:41 | 顯示全部樓層
學(xué)習(xí)了,謝謝樓主!!
回復(fù)

使用道具 舉報(bào)

ID:737697 發(fā)表于 2020-4-25 14:10 | 顯示全部樓層
學(xué)習(xí)了,謝謝分享
回復(fù)

使用道具 舉報(bào)

ID:276663 發(fā)表于 2020-4-25 17:15 | 顯示全部樓層
多謝分享,很有參考意義
回復(fù)

使用道具 舉報(bào)

ID:142652 發(fā)表于 2020-4-26 11:14 | 顯示全部樓層
干貨滿(mǎn)滿(mǎn)哦,謝謝!
回復(fù)

使用道具 舉報(bào)

ID:738622 發(fā)表于 2020-4-26 16:51 | 顯示全部樓層
很有份量的分享,受教了
回復(fù)

使用道具 舉報(bào)

ID:738950 發(fā)表于 2020-4-26 23:37 | 顯示全部樓層
stm32正點(diǎn)原子和野火配合著看學(xué)習(xí)最好
回復(fù)

使用道具 舉報(bào)

ID:739404 發(fā)表于 2020-4-27 16:04 | 顯示全部樓層
厲害! 感謝分享
回復(fù)

使用道具 舉報(bào)

ID:320751 發(fā)表于 2020-5-1 20:29 | 顯示全部樓層
謝謝詳細(xì)講解
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

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

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