在開始正式寫代碼之前,首先要解決一件事,那就是我們該如何去寫這代碼。
在樹莓派搭建的Linux環(huán)境中,使用基于Xorg的桌面環(huán)境在本地開啟圖形化界面開發(fā)太耗費系統(tǒng)資源,這個方案首先不考慮。不過還剩下兩種其他可行的方案,一是直接通過終端遠程登陸進行開發(fā),開發(fā)工作全部在文本模式下進行;另一種則使用Samba服務將Linux的文件系統(tǒng)掛載到Windows中,然后在Windows上面使用圖形化界面的IDE開發(fā)。
如果你比較Geek,你可能會嘗試遠程登陸的方式,通過Vim或者Emacs兩大神器之一進行開發(fā)。不過他們兩者對新手不夠友好,具體可以自己體驗一下,不過網(wǎng)上有個段子:
問:如何生成一個隨機字符串?
答:讓新手退出vim.
而且在沒有深入了解二者之前,容易被他們之間的“世紀大戰(zhàn)”搞迷糊,然后進入一種著相1的狀態(tài):一定要對他們二者分一個高下,然后選擇一個最好的使用。其實它們只是工具而已,自己用著爽就行了,不要自己把自己加入到程序員的鄙視鏈中,以開放的心態(tài)看待事物。筆者個人習慣使用Vim(別問,問就Vim),有豐富的插件,也可以自己對它擴展。或者試試VimPlus,一鍵幫你打造功能強大的Vim IDE(真香警告)。
VimPlus界面
不過本系列是面向新同學的技術科普文章,在這里選用相對簡單一點的方式,使用Samba服務將樹莓派的文件系統(tǒng)通過網(wǎng)絡掛載到Windows上,在Windows中使用VS Code進行開發(fā)。相比于Vim,VSCode使用起來簡直不要太絲滑了。
首先在樹莓派中安裝Samba服務:
$ sudo apt install samba smbclient
安裝samba
安裝完成之后,需要配置它:
$ sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak $ sudo nano /etc/samba/smb.conf
在末尾添加,并保存:
[pi] comment = user pi shared path = /home/pi available = yes browseable = yes public = yes writable = yes
然后創(chuàng)建samba用戶名和密碼:
$ sudo touch /etc/samba/smbpasswd $ sudo smbpasswd -a pi
然后會提示為這個用戶創(chuàng)建一個新密碼,可以隨便指定,這里為了方便也設置為“pi”。完成之后要重啟samba服務:
$ sudo systemctl restart smbd.service
此時Samba服務已經(jīng)可用了,接下來在Windows中對其進行掛載:
此電腦->計算機->映射網(wǎng)絡驅(qū)動器
輸入samba服務地址
輸入用戶名和密碼
網(wǎng)絡驅(qū)動器映射成功
請注意填寫自己樹莓派獲取到的IP。如果出現(xiàn)異常,請看參考資料[1]嘗試解決或者谷歌,還不行的話可以在文章下面評論或者私聊我~
接下來咋裝VSCode我就不介紹了,把它裝好之后我們就開始做點燈實驗,首先在樹莓派上把我準備的源碼倉庫拉下來,倉庫中我會對每一個能跑的實例代碼都會在相應的commit上面打上一個tag:
$ git clone https://github.com/Virus-V/Raspberry_STM32.git $ cd Raspberry_STM32 $ git checkout led # 切換到 led tag 對應的commit
接下來執(zhí)行編譯并燒錄到STM32中,燒錄的時候要保證STM32開發(fā)板已經(jīng)連接到樹莓派上,同時lsusb也能看到它:
~/Raspberry_STM32$ make -j
Build modules...
[...]
arm-none-eabi-objcopy -O binary led.elf led.bin
arm-none-eabi-objdump -h -S -D led.elf > led.lss
arm-none-eabi-nm -n led.elf > led.sym
Size after
led.elf :
section size addr
.isr_vector 268 134217728
.text 1172 134217996
.data 0 536870912
.bss 0 536870912
._user_heap_stack 512 536870912
.ARM.attributes 51 0
.debug_info 5639 0
.debug_abbrev 1581 0
.debug_aranges 560 0
.debug_ranges 496 0
.debug_line 2799 0
.debug_str 2652 0
.comment 43 0
.debug_frame 2324 0
Total 18097
Make Complete~
Sun Mar 15 02:34:41 UTC 2020
~/Raspberry_STM32$ sudo make download
Downloading led ...
Open On-Chip Debugger 0.10.0+dev-g4cfb2d54 (2020-03-12-12:36)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport 'hla_swd'. To override use 'transport select Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD Info : clock speed 1000 kHz Info : STLINK V2J24M11 (API v2) VID:PID 0483:374B Info : Target voltage: 3.230159 Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : Listening on port 3333 for gdb connections target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000404 msp: 0x20005000 ** Programming Started ** Info : device id = 0x20036410 Info : flash size = 128kbytes ** Programming Finished ** ** Verify Started ** ** Verified OK ** ** Resetting Target ** shutdown command invoked 此時編譯生成的代碼已經(jīng)下載到開發(fā)板中,不出意外的話,LED已經(jīng)開始閃爍了。 在開發(fā)板上運行 現(xiàn)在LED已經(jīng)被成功點亮了,how it works?我們來扒一扒它內(nèi)部的原理究竟是什么。 首先,我們編寫的代碼在Raspberry_STM32/User/main.c中,它里面有三個函數(shù):main, delay和Init_LED。main是我們程序邏輯的入口,里面首先調(diào)用Init_LED函數(shù),初始化了GPIO外設。接下來就是一個大循環(huán),里面重復執(zhí)行著點亮——延時——熄滅——延時,如此往復。 現(xiàn)在看起來歲月靜好,燈也按著我們的想法如期閃爍起來了。 代碼下載到了哪里?芯片斷電之后代碼還在嗎? 芯片是從main開始執(zhí)行第一行代碼的嗎?如果不是,哪里才是它的第一行代碼? 芯片怎么知道它要執(zhí)行的第一行代碼在哪里?代碼是怎么執(zhí)行起來的? 為什么我能通過代碼控制LED燈? 回答這些問題之前,我們先看看STM32F103這個小東西肚子里都有什么。 STM32F103內(nèi)部結(jié)構(gòu)框圖 可以看到,STM32雖然黑不溜秋的一小坨趴在電路板上,它肚子里的東西還是很多的。 其實我們的代碼下載到了片上Flash里,也就是上圖標綠色的部分,這個東西是STM32片上的一塊只讀存儲空間(ROM),斷電之后不會丟失的。 STM32上電之后,首先從中斷向量表中設置棧的起始位置,然后跳到中斷向量表中的Reset_Handler執(zhí)行系統(tǒng)復位事件處理函數(shù),它主要是初始化全局變量,將全局變量從ROM中搬移到RAM(上圖標藍色的部分),并且對BSS空間進行清零。BSS空間可以看作是默認值為0的全局變量所在的位置。全局變量初始化完成之后,調(diào)用SystemInit對芯片進行初始化:打開外部高速時鐘,初始化鎖相環(huán)倍頻至72MHz,初始化AHB總線時鐘,APB1時鐘和APB2時鐘等初始化工作。最終才會調(diào)到我們編寫的main函數(shù)中。 那么芯片怎么知道中斷向量表在哪里呢?這個其實不難回答,中斷向量表的地址是固化到芯片里的,而我們需要做的是把我們代碼中的中斷向量表放在那個固定的位置就OK了,放置的細節(jié)由鏈接器腳本文件來控制,這個文件是CMSIS/flash.ld。芯片上電之后就會在那個位置取第一行代碼去執(zhí)行。 Cortex-M3是單發(fā)射順序執(zhí)行的標量處理器,內(nèi)部是三級流水線,一個指令需要經(jīng)過取指,譯碼,執(zhí)行三個階段才可以產(chǎn)生結(jié)果,然后退休(retire)。而指令執(zhí)行過程中需要有寄存器配合才能做一些數(shù)據(jù)的處理。寄存器分為通用寄存器和專用寄存器。通用寄存器可以做任何用途,這些寄存器都由編譯器分配如何使用,如果你手撕匯編的話最好遵循ATPCS或其他相應的調(diào)用規(guī)范;而專用寄存器是維護當前程序指令執(zhí)行流狀態(tài)的,包括棧頂指針SP,分支跳轉(zhuǎn)返回地址LR,當前指令地址PC等。 正是因為這些處理器基礎設施,才得以我們的代碼有條不紊歡快地運行。 而最后一個問題,為什么我能通過代碼控制LED燈呢?首先,LED連接到芯片IO口的引腳上,這個IO口屬于GPIO,而GPIO作為一個外設(peripheral)掛到了APB2總線上。掛載到總線上的外設都有一系列寄存器,這些寄存器在整個32位的地址空間中都有一個唯一的地址,而我們的指令正是通過這些寄存器的地址來訪問外設的這些配置寄存器,對其進行讀寫,從而達到了控制的目的。在GPIO中,一個IO口引腳對應其輸出寄存器的一個bit,只要用指令將這個二進制位設置為0或者1,就可以控制相應的IO輸出高電平或低電平。它內(nèi)部實現(xiàn)的細節(jié)在此不深究,這屬于數(shù)字電路的范疇,總之我們代碼對外設所有的操作,都可以轉(zhuǎn)化為對某個地址的讀或?qū)憽?p>這也就讓我想起了Unix設計哲學:一切皆文件,一切交互都可以抽象成讀或者寫。 int main() {
Init_LED();
while(1) {
GPIO_SetBits(GPIOC, GPIO_Pin_1);
delay();
GPIO_ResetBits(GPIOC, GPIO_Pin_1);
delay();
}
return 0;}
但是,眼前的歲月靜好,是因為有人在背后替我們負重前行。我們來思考幾個問題:
STM32的第一行代碼不是main函數(shù),如果你仔細觀察過編譯過程,會發(fā)現(xiàn)有一個名為startup_stm32f10x_md.s的文件被編譯并鏈接到最終的可執(zhí)行程序里,它才是芯片啟動后第一行代碼所在的那個文件。 .section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call the application's entry point.*/
bl main
bx lr
上一篇:STM32的Bootloader
下一篇:STM32:從菜鳥到牛人就這么簡單~
推薦閱讀最新更新時間:2025-07-02 09:18




設計資源 培訓 開發(fā)板 精華推薦
- Microchip 升級數(shù)字信號控制器(DSC)產(chǎn)品線 推出PWM 分辨率和 ADC 速度業(yè)界領先的新器件
- 意法半導體STM32MP23x:突破成本限制的工業(yè)AI應用核心
- 意法半導體推出用于匹配遠距離無線微控制器STM32WL33的集成的匹配濾波芯片
- ESP32開發(fā)板連接TFT顯示屏ST7789跳坑記
- 如何讓ESP32支持analogWrite函數(shù)
- LGVL配合FreeType為可變字體設置字重-ESP32篇
- 使用樹莓派進行 ESP32 Jtag 調(diào)試
- ESP32怎么在SPIFFS里面存儲html,css,js文件,以及網(wǎng)頁和arduino的通訊
- ESP32 freeRTOS使用測試
- LTC2194IUKG、16 位、105Msps 低功耗雙通道 ADC 的典型應用
- LT3088HST 并聯(lián)穩(wěn)壓器的典型應用
- 具有外部控制信號、AVIN 或 AGND 的 EP53A7LQI 1A 同步降壓穩(wěn)壓器的典型應用電路
- LTC4367HDD-1 用于限制浪涌電流的過壓電源保護控制器的典型應用
- 基于ST1S03的1.5 A / 3.3 V降壓DC-DC轉(zhuǎn)換器演示板
- 具有基于STAP08DP05和STM8AF的汽車應用診斷的高亮度LED陣列驅(qū)動器
- L7808C 高輸入輸出穩(wěn)壓器的典型應用
- LT6657BHMS8-2.5 低壓差基準電壓源的典型應用電路
- 使用 Analog Devices 的 LTC3423EMS 的參考設計
- AD9261-10EBZ,AD9261 評估板,16 位連續(xù)時間 Sigma-Delta ADC
- 潤石科技推出RS3215-Q1系列低壓差線性穩(wěn)壓器
- CANape 23開啟智能測試新時代(下)
- 軟件定義汽車電源管理設計:NXP的PMIC選型攻略解析
- 深度測評時空壺X1同聲傳譯器:全球最先進AI同傳設備的非凡實力
- 作為領先的垂直整合制造商(IDM),英飛凌在 300mm氮化鎵生產(chǎn)路線圖方面取得突破
- 鐵威馬F6-424 Max:六盤位擴容+國產(chǎn)系統(tǒng)兼容,小白也能玩轉(zhuǎn)NAS
- 開關電源給鉛酸電池恒流恒壓充電的電流跳動問題解析
- 自動駕駛中的激光雷達是否會傷害攝像頭?
- 觸覺技術助力企業(yè)在激烈的市場競爭中脫穎而出
- 芯片中的串擾噪聲有幾類