目錄
- LibOpenCM3(一) Linux下命令行開發環境配置
- LibOpenCM3(二) 項目模板 Makefile分析
- LibOpenCM3(三) .ld文件(連接器腳本)和startup代碼說明
- LibOpenCM3(四) VSCode IDE 環境配置
- LibOpenCM3(五) 基礎功能: 系統時鍾, GPIO, 定時器
本文默認使用 Linux 環境, 硬件為 STM32F103 系列開發板
LibOpenCM3 介紹
LibOpenCM3 是GPL協議(LGPL3)的Cortex-M系列的封裝庫, 支持stm32、atmel、nxp系列單片機. 這個固件庫對標的是 CMSIS, 但是比 CMSIS 提供更多的方法接口, 實現度介於 CMSIS 和 SPL 之間. 對於常見的 STM32F1 系列, 代碼已經基本穩定.
- 項目地址
https://github.com/libopencm3/libopencm3 - 項目示例
https://github.com/libopencm3/libopencm3-examples - WIKI
https://github.com/libopencm3/libopencm3/wiki - API文檔
http://libopencm3.org/docs/latest/html/
開發環境
硬件部分
- STM32F103 開發板
- ST-Link V2
軟件部分
- LibOpenCM3
https://github.com/libopencm3/libopencm3 - LibOpenCM3項目模板
https://github.com/libopencm3/libopencm3-template - GNU Arm Embedded Toolchain
下載地址 - stlink linux工具, 包含st-flash,st-info等命令
https://github.com/stlink-org/stlink
環境配置
GNU Arm Embedded Toolchain 工具鏈
通過前面的地址下載最新版本, 根據自己的系統架構選擇合適的架構版本
# 解壓
tar xjf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2
# 放到/opt下並設置權限
sudo mv gcc-arm-none-eabi-10.3-2021.10 /opt/gcc-arm/
cd /opt/gcc-arm/
sudo chown -R root:root gcc-arm-none-eabi-10.3-2021.10/
加到PATH(可選)
export PATH="/opt/gcc-arm/gcc-arm-none-eabi-10.3-2021.10/bin:$PATH"
檢查版本
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10) 10.3.1 20210824 (release)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
stlink,st-flash,st-info
需要先安裝 libusb-1.0-0-dev
sudo apt install libusb-1.0-0-dev
編譯安裝
git clone https://github.com/stlink-org/stlink.git
cd stlink/
make
cd build/Release/
sudo make install
檢查
$ st-flash --version
v1.7.0-184-g468b1d2
$ st-info --version
Failed to parse flash type or unrecognized flash type
v1.7.0-184-g468b1d2
st-flash 使用說明 https://github.com/stlink-org/stlink/blob/develop/doc/tutorial.md
- --flash=128k 參數可以指定flash大小, 例如覆蓋 STM32F103C8T6 默認的 64k 大小設置. 取值可以使用 十進制(128k), 八進制 0200k, 或者十六進制 0x80k.
- --reset 在寫入結束后觸發重置
- --connect-under-reset 重置狀態下連接. 使用這個選項可以在用戶代碼執行前連接MCU, 當目標MCU上的用戶代碼將MCU置於sleep狀態, 或禁用了調試接口導致無法連接時特別有用.
LibOpenCM3
LibOpenCM3封裝庫文件結構
libopencm3
├── COPYING.GPL3
├── COPYING.LGPL3
├── doc
├── HACKING
├── HACKING_COMMON_DOC
├── include # 頭文件目錄
│ ├── libopencm3
│ │ ├── cm3
│ │ ├── dispatch
│ │ ├── docmain.dox
│ │ ├── efm32
│ │ ├── ethernet
│ │ ├── gd32
│ │ ├── license.dox
│ │ ├── lm3s
│ │ ├── lm4f
│ │ ├── lpc13xx
│ │ ├── lpc17xx
│ │ ├── lpc43xx
│ │ ├── msp432
│ │ ├── nrf
│ │ ├── pac55xx
│ │ ├── sam
│ │ ├── stm32 # 按不同的型號分組的頭文件, 定義中斷向量表的 irq.json
│ │ ├── swm050
│ │ ├── usb
│ │ └── vf6xx
│ └── libopencmsis
│ ├── core_cm3.h
│ └── dispatch
│ └── irqhandlers.h
├── ld
│ ├── devices.data # 芯片數據庫, 各個芯片型號對應的flash和ram尺寸
│ ├── linker.ld.S # 連接器腳本模板, 不是匯編代碼
│ ├── README
│ └── tests
├── lib # 鏈接庫目錄, 編譯后生成的.a文件會放到對應芯片目錄下
│ ├── cm3
│ │ ├── assert.c
│ │ ├── dwt.c
│ │ ├── nvic.c
│ │ ├── scb.c
│ │ ├── sync.c
│ │ ├── systick.c
│ │ └── vector.c # startup代碼
│ ├── cortex-m-generic.ld
│ ├── dispatch
│ │ ├── vector_chipset.c
│ │ └── vector_nvic.c
│ ├── efm32
│ ├── ethernet
│ ├── gd32
│ ├── lm3s
│ ├── lm4f
│ ├── lpc13xx
│ ├── lpc17xx
│ ├── lpc43xx
│ ├── Makefile.include
│ ├── msp432
│ ├── nrf
│ ├── pac55xx
│ ├── sam
│ ├── stm32
│ ├── swm050
│ ├── usb
│ └── vf6xx
├── locm3.sublime-project
├── Makefile
├── mk # make 規則目錄
│ ├── gcc-config.mk
│ ├── gcc-rules.mk
│ ├── genlink-config.mk # 用於配置生成ld需要的參數
│ ├── genlink-rules.mk # 用於生成ld文件
│ └── README
├── README.md
├── scripts
│ ├── checkpatch.pl
│ ├── data
│ ├── gendoxylayout.py
│ ├── gendoxylist
│ ├── genlink.py # 芯片數據庫文件搜索腳本
│ ├── genlinktest.sh # 芯片數據庫文件搜索測試腳本
│ ├── irq2nvic_h # 讀取irq.json, 填充IRQ_HANDLERS變量, 這個變量會被vector.c 的 vector_table 引用,
│ └── lpcvtcksum
└── tests
LibOpenCM3 項目模板
導出 libopencm3 可以單獨編譯, 但是配置為完整的項目還需要添加用戶代碼和Makefile, 因為 LibOpenCM3 已經提供了立即可用的項目模板, 可以直接用模板提供的環境進行開發
導出模板
git clone --recurse-submodules https://github.com/libopencm3/libopencm3-template.git
cd libopencm3-template/
# 這一步需要前面的 arm-none-eabi-gcc 工具鏈已經配置到 PATH
# 編譯
make -C libopencm3
這一步會編譯 libopencm3
- 在 libopencm3/lib 目錄下生成全部型號的 .a 庫文件
- 在 libopencm3/lib/stm32 目錄下, 對應 f0, f1, ..., f7 等各個目錄下生成對應型號的 ld 文件
如果前面沒有將 arm-none-eabi-gcc 添加到PATH, 需要修改一下 libopencm3/Makefile, 將 PREFIX 修改為
PREFIX ?= /opt/gcc-arm/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-
如果只想編譯當前使用的設備(STM32F103), 可以指定設備, 這樣比較節省時間
TARGETS=stm32/f1 make
如果要輸出完整命令, 可以加上 V=1 參數
TARGETS=stm32/f1 make V=1
注: 在當前的模板項目中, libopencm3不是最新版本, 版本號 cb0661f81de5b1cae52ca99c7b5985b678176db7 對應的是2020-02-16的版本, 這個版本在lib/stm32/fx 目錄下依然有各個型號的ld文件, 而這些文件實際上在 2020-11-29 的提交 BREAKING: drop all part specific ld files中已經刪除了. 具體的說明可以點開查看. 當前模板的Makefile實際上使用的是后者的編譯方式生成項目的ld文件.
編輯用戶項目, 在編輯之前, 需要修改 my-project 目錄下的 Makefile, 將 DEVICE 修改為自己的 STM32 型號
# TODO - you will need to edit these two lines!
DEVICE=stm32f103c6t6
填入的型號如果正確, 會在編譯時生成對應的ld文件, 例如對應上面的就是 generated.stm32f103c6t6.ld
如果前面沒有將 arm-none-eabi-gcc 添加到PATH, 需要修改一下 my-project/Makefile, 添加 PREFIX 定義
PREFIX ?= /opt/gcc-arm/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-
編譯
make -C my-project
這時候在 my-project 目錄下會產生以下文件
total 172
-rwxrwxr-x 1 milton milton 1492 Feb 20 23:55 awesomesauce.bin*
-rwxrwxr-x 1 milton milton 276952 Feb 20 23:55 awesomesauce.elf*
drwxrwxr-x 2 milton milton 4096 Feb 20 23:55 bin/
-rw-rw-r-- 1 milton milton 1108 Feb 20 23:55 generated.stm32f103c6t6.ld
-rw-rw-r-- 1 milton milton 493 Feb 20 23:38 Makefile
-rw-rw-r-- 1 milton milton 2099 Feb 20 23:42 my-project.c
其中 awesomesauce.bin 是用於燒錄的文件
通過 arm-none-eabi-size 查看編譯結果的內存結構.
$ arm-none-eabi-size awesomesauce.elf
text data bss dec hex filename
1480 12 0 1492 5d4 awesomesauce.elf
其中
- text 是占用的flash空間
- data 初始化占用內存大小
- bss 全局分配的內存大小
- dec和hex 十進制和十六進制表示的總大小
演示示例
下面用一個閃燈的示例把開發環境跑一遍.
用戶代碼
將 my-project 目錄下的 my-project.c 內容修改為
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/timer.h>
#ifndef ARRAY_LEN
#define ARRAY_LEN(array) (sizeof((array))/sizeof((array)[0]))
#endif
#define LED1_PORT GPIOC
#define LED1_PIN GPIO13
static void gpio_setup(void)
{
/* Enable GPIO clock for leds. */
rcc_periph_clock_enable(RCC_GPIOC);
/* Enable led as output */
gpio_set_mode(
LED1_PORT,
GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
LED1_PIN);
gpio_set(LED1_PORT, LED1_PIN);
}
static void tim_setup(void)
{
/* Enable TIM2 clock. */
rcc_periph_clock_enable(RCC_TIM2);
/* Enable TIM2 interrupt. */
nvic_enable_irq(NVIC_TIM2_IRQ);
/* Reset TIM2 peripheral to defaults. */
rcc_periph_reset_pulse(RST_TIM2);
/* Timer global mode:
* - No divider
* - Alignment edge
* - Direction up
* (These are actually default values after reset above, so this call
* is strictly unnecessary, but demos the api for alternative settings)
*/
timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
/* Disable preload. */
timer_disable_preload(TIM2);
timer_continuous_mode(TIM2);
timer_set_prescaler(TIM2, 36000); // Clock counts every 0.5 msec
timer_set_period(TIM2, 2000); // 2000 * 0.5 msec => 1 sec
/* Counter enable. */
timer_enable_counter(TIM2);
/* Enable Channel 1 compare interrupt to recalculate compare values */
timer_enable_irq(TIM2, TIM_DIER_CC1IE);
}
/**
* ISR method defined in libopencm3/include/libopencm3/stm32/f1/nvic.h
*/
void tim2_isr(void)
{
if (timer_get_flag(TIM2, TIM_SR_CC1IF))
{
/* Clear compare interrupt flag. */
timer_clear_flag(TIM2, TIM_SR_CC1IF);
/* Toggle LED to indicate compare event. */
gpio_toggle(LED1_PORT, LED1_PIN);
}
}
int main(void)
{
// Setup main clock, using external 8MHz crystal
rcc_clock_setup_in_hse_8mhz_out_72mhz();
// 如果用的libopencm3版本較高, 編譯提示上面的函數已經廢棄, 可以換成下面的代碼
//rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]);
gpio_setup();
tim_setup();
while (1);
return 0;
}
上面的代碼, 用TIM2定時, 觸發板載C13對應的LED每秒切換亮滅狀態
編譯
使用前面配置好的開發環境
make -C my-project
寫入
將開發板接上ST-Link接上PC
寫入, 0x800 0000 是 STM32F103 的Flash空間起始地址
st-flash write ./awesomesauce.bin 0x8000000
# 會看到包含如下文字的輸出
2022-02-21T00:00:42 INFO common.c: STM32F1xx_LD: 10 KiB SRAM, 32 KiB flash in at least 1 KiB pages.
file ./awesomesauce.bin md5 checksum: 363668176fa21306a846725b7db2079, stlink checksum: 0x0001fe1e
2022-02-21T00:00:42 INFO common_flash.c: Attempting to write 1492 (0x5d4) bytes to stm32 address: 134217728 (0x8000000)
-> Flash page at 0x8000000 erased (size: 0x400)
-> Flash page at 0x8000400 erased (size: 0x400)
2022-02-21T00:00:42 INFO flashloader.c: Starting Flash write for VL/F0/F3/F1_XL
2022-02-21T00:00:42 INFO flash_loader.c: Successfully loaded flash loader in sram
2022-02-21T00:00:42 INFO flash_loader.c: Clear DFSR
2022-02-21T00:00:42 INFO flash_loader.c: Clear CFSR
2022-02-21T00:00:42 INFO flash_loader.c: Clear HFSR
2/ 2 pages written
2022-02-21T00:00:42 INFO common_flash.c: Starting verification of write complete
2022-02-21T00:00:42 INFO common_flash.c: Flash written and verified! jolly good!
此時需要手動按一下開發板的 RESET 鍵, 程序才會開始執行, 如果需要自動重置, 使用下面的命令
st-flash --reset write ./awesomesauce.bin 0x8000000
其它命令
查看開發板信息
st-info --probe
# 會看到包含如下文字的輸出(當前這塊測試用的是 stm32f103c6t6)
Found 1 stlink programmers
version: V2J37S7
serial: 56FF6B064966485627461667
flash: 32768 (pagesize: 1024)
sram: 10240
chipid: 0x412
擦除
st-flash erase
# 會看到包含如下文字的輸出
2022-02-21T00:00:12 INFO common.c: STM32F1xx_LD: 10 KiB SRAM, 32 KiB flash in at least 1 KiB pages.
Mass erasing