使用gcc和c++開發stm32

緒論

從事於嵌入式軟件開發已經有7個年頭了,這些年一直在迴避一個問題,就是寫文檔,總覺得寫文檔是一件非常痛苦的事情,而且還要花大把時間,爲什麼要寫文檔? 直到最近才發現,隨着年齡的不斷增加,髮量的減少,記性也越來越差,所以這時文檔就非常有幫助了,在上一個項目結束,新項目還沒啓動的空窗期,決定學習下ubuntu上開發stm32,並學習下使用c++,廢話結束,開整!

準備工作

安裝 GNU ARM 工具鏈

Ubuntu平臺

Ubuntu平臺可以使用命令sudo apt-get install gcc-arm-none-eabi命令直接安裝,但是目前包裏的版本是比較老的,而且沒有arm-none-eabi-gdb工具進行gdb調試,所以建議手動下載安裝。

  1. 直接前往官網 https://developer.arm.com下載。
  2. 將其解壓到 /usr/bin/gcc目錄下,或者其他位置,
  3. 修改 ~/.bashrc,加入export PATH="$PATH:/usr/lib/gcc/gcc-arm-none-eabi-8-2019-q3-update/bin"
  4. 使用命令source ~/.bashrc使能最新更改。
  5. arm-none-eabi-gcc -v查看版本號,若能正確顯示版本號,就表示安裝成功。

Windows平臺

Windows平臺直接前往官網 https://developer.arm.com下載對應windows平臺的應用程序即可,唯一需要注意的是需要把arm工具鏈的bin文件夾加入環境變量PATH中。

安裝 JLink 驅動

本次開發使用的是Segger公司的JLink作爲調試下載工具。

Ubuntu平臺

  1. 前往官網 https://www.segger.com/下載與之對應的應用程序即可,下載後直接安裝。
  2. 在終端上輸入JLinkExe 若能顯示版本號即表示安裝成功

Windows平臺

Windows平臺就不廢話了,同樣的,需要將其加入到環境變量PATH中。

安裝 MinGW

還是簡單介紹下MinGW,MinGW全稱是Minimalist GNU for Windows,簡單來說就是爲了在Windows平臺上使用make命令及其他的一些GNU工具。

Ubuntu平臺

無需安裝。

Windows 平臺

前往官網 http://www.mingw.org/下載,同樣需要將其加入換將變量。

第一個項目LED

撰寫C++源代碼

建一個抽象類CGPIO,定義方法writeread

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)

結尾

好吧,最後也是草草的就結尾了,果然還是不喜歡寫文檔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章