目錄
緒論
從事於嵌入式軟件開發已經有7個年頭了,這些年一直在迴避一個問題,就是寫文檔,總覺得寫文檔是一件非常痛苦的事情,而且還要花大把時間,爲什麼要寫文檔? 直到最近才發現,隨着年齡的不斷增加,髮量的減少,記性也越來越差,所以這時文檔就非常有幫助了,在上一個項目結束,新項目還沒啓動的空窗期,決定學習下ubuntu上開發stm32,並學習下使用c++,廢話結束,開整!
準備工作
安裝 GNU ARM 工具鏈
Ubuntu平臺
Ubuntu平臺可以使用命令sudo apt-get install gcc-arm-none-eabi
命令直接安裝,但是目前包裏的版本是比較老的,而且沒有arm-none-eabi-gdb工具進行gdb調試,所以建議手動下載安裝。
- 直接前往官網 https://developer.arm.com下載。
- 將其解壓到 /usr/bin/gcc目錄下,或者其他位置,
- 修改 ~/.bashrc,加入
export PATH="$PATH:/usr/lib/gcc/gcc-arm-none-eabi-8-2019-q3-update/bin"
。 - 使用命令
source ~/.bashrc
使能最新更改。 arm-none-eabi-gcc -v
查看版本號,若能正確顯示版本號,就表示安裝成功。
Windows平臺
Windows平臺直接前往官網 https://developer.arm.com下載對應windows平臺的應用程序即可,唯一需要注意的是需要把arm工具鏈的bin文件夾加入環境變量PATH中。
安裝 JLink 驅動
本次開發使用的是Segger公司的JLink作爲調試下載工具。
Ubuntu平臺
- 前往官網 https://www.segger.com/下載與之對應的應用程序即可,下載後直接安裝。
- 在終端上輸入
JLinkExe
若能顯示版本號即表示安裝成功
Windows平臺
Windows平臺就不廢話了,同樣的,需要將其加入到環境變量PATH中。
安裝 MinGW
還是簡單介紹下MinGW,MinGW全稱是Minimalist GNU for Windows,簡單來說就是爲了在Windows平臺上使用make命令及其他的一些GNU工具。
Ubuntu平臺
無需安裝。
Windows 平臺
前往官網 http://www.mingw.org/下載,同樣需要將其加入換將變量。
第一個項目LED
撰寫C++源代碼
建一個抽象類CGPIO
,定義方法write
和 read
class CGPIO
{
public:
/** write method */
virtual void write(uint8_t val) = 0;
/** read method */
virtual uint8_t read(void) = 0;
};
派生一個CStm32GPIO
,頭文件
class CStm32GPIO: public CGPIO
{
public:
/** enum GPIO port */
enum { PORTA, PORTB, PORTC, PORTD, PORTE, PORTF };
/** enum GPIO pin */
enum {
GPIO_Pin_0 = 0x00,
GPIO_Pin_1,
GPIO_Pin_2,
GPIO_Pin_3,
GPIO_Pin_4,
GPIO_Pin_5,
GPIO_Pin_6,
GPIO_Pin_7,
GPIO_Pin_8,
GPIO_Pin_9,
GPIO_Pin_10,
GPIO_Pin_11,
GPIO_Pin_12,
GPIO_Pin_13,
GPIO_Pin_14,
GPIO_Pin_15,
};
/** enum GPIO mode */
enum {
GPIO_Mode_IN = 0X00,
GPIO_Mode_OUT = 0X01,
GPIO_Mode_AF = 0x02,
GPIO_Mode_AN = 0x03,
};
/** enum GPIO PuPd */
enum {
GPIO_PuPd_NOPULL = 0x00,
GPIO_PuPd_UP = 0x01,
GPIO_PuPd_DOWN = 0x02,
};
public:
/** public method */
CStm32GPIO(uint8_t port, uint16_t pin, uint8_t mode = GPIO_Mode_OUT, uint8_t pupd = GPIO_PuPd_UP);
~CStm32GPIO();
void write(uint8_t val);
uint8_t read(void);
public:
/** public variables */
private:
/** private method */
private:
/** private variables */
uint8_t m_Port;
uint16_t m_Pin;
};
CStm32GPIO::CStm32GPIO(uint8_t port, uint16_t pin, uint8_t mode, uint8_t pupd)
{
GPIO_TypeDef *GPIOx;
m_Port = port;
m_Pin = pin;
/* enable AHB clock */
RCC->AHB1ENR |= (uint32_t)((0x01) << port);
/* Get GPIOx register address */
GPIOx = (GPIO_TypeDef *)(AHB1PERIPH_BASE + 0x0400 * port);
GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pin * 2));
GPIOx->MODER |= (mode << (pin * 2));
if(( mode == GPIO_Mode_OUT) || (mode == GPIO_Mode_AF))
{
/* speed mode configuation */
GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pin * 2));
GPIOx->OSPEEDR |= (0x03 << (pin *2)); // hight speed
GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pin)) ;
GPIOx->OTYPER |= (mode << pin);
}
GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pin * 2));
GPIOx->PUPDR |= (((uint32_t)pupd) << (pin * 2));
}
CStm32GPIO::~CStm32GPIO()
{
}
void CStm32GPIO::write(uint8_t val)
{
GPIO_TypeDef *GPIOx;
/* Get GPIOx register address */
GPIOx = (GPIO_TypeDef *)(AHB1PERIPH_BASE + 0x0400 * m_Port);
if(val != 0)
{
GPIOx->BSRRL = (0x01 << m_Pin);
}
else
{
GPIOx->BSRRH = (0x01 << m_Pin);
}
}
uint8_t CStm32GPIO::read(void)
{
GPIO_TypeDef *GPIOx;
/* Get GPIOx register address */
GPIOx = (GPIO_TypeDef *)(AHB1PERIPH_BASE + 0x0400 * m_Port);
return (GPIOx->IDR & (0x01 << m_Pin)) ? 1 : 0;
}
再定義一個led
類
class CLed
{
public:
CLed(CGPIO *pGPIO);
~CLed();
void on(void);
void off(void);
private:
CGPIO *m_pGPIO;
};
接着寫main
函數
void delay(uint32_t times)
{
uint32_t i, j;
for( i = 0; i < times; i++)
{
for( j = 0; j < 10000; j++ )
{
__NOP();
}
}
}
int main(void)
{
CStm32GPIO gpioRed(CStm32GPIO::PORTF, CStm32GPIO::GPIO_Pin_9);
CStm32GPIO gpioGreen(5, 10);
CLed ledRed(&gpioRed);
CLed ledGreen(&gpioGreen);
for(;;)
{
ledRed.on();
ledGreen.off();
delay(1000);
ledRed.off();
ledGreen.on();
delay(1000);
}
/* never reach here */
return 0;
}
Makefile
源代碼寫好了,開始寫Makefile
# source file
CCSRCS := system_stm32f4xx.c
ACSRCS := startup_stm32f40_41xxx.s
CXXSRCS := main.cpp stm32_gpio.cpp led.cpp
SUBDIR :=
# current makefile path
mk_path := $(abspath $(lastword $(MAKEFILE_LIST)))
# global variables
export ROOTDIR := $(patsubst %/,%,$(dir $(mk_path)))
export ODIR := $(ROOTDIR)/out
export COMMON_MK = $(ROOTDIR)/Makefile.common
export INCDIR := cmsis
INCDIR := $(addprefix $(ROOTDIR)/, $(INCDIR)) $(ROOTDIR)
export DEFINE := USE_STDPERIPH_DRIVER STM32F40_41xxx HSE_VALUE=8000000
VPATH = $(INCDIR)
AOBJS :=
# output files
PROJECT := $(ODIR)/TC21
TARGET_ELF := $(PROJECT).elf
TARGET_BIN := $(PROJECT).bin
TARGET_HEX := $(PROJECT).hex
# TODO: find library path automatically.
ifeq ($(shell uname),Linux)
JLink := JLinkExe
else
JLink := JLink.exe
endif
# recursive wildcard
rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2))$(filter $(subst *,%,$(2)),$(d)))
.PHONY: build find_objs
build: $(TARGET_BIN) $(TARGET_HEX)
@echo "Finished!!!"
include $(COMMON_MK)
$(TARGET_HEX):$(TARGET_ELF)
@echo "Creating $@ ...."
$(ELF2HEX)
$(TARGET_BIN):$(TARGET_ELF)
@echo "Creating $@ ...."
$(ELF2BIN)
$(TARGET_ELF): $(SUBDIR) sbuild find_objs
@echo "Linking $(notdir $@)..."
@$(LD) $(AOBJS) $(LDFLAGS) $(LDLIBS) -Wl,-Map=$(@:.elf=.map) -o $@
@$(SIZE) $@
find_objs:
$(eval AOBJS = $(call rwildcard,$(ODIR),*.o))
# program
.PHONY: program
program:
@$(JLink) jlink_program.txt
# phony
.PHONY: clean
clean:
@-rm -rf $(ODIR)
結尾
好吧,最後也是草草的就結尾了,果然還是不喜歡寫文檔。