? ? STM32單片機相比51單片機,內部結構復雜很多,因此直接對底層寄存器編碼,相對復雜,這個需要我們了解芯片手冊,對于復雜項目,這些操作可能需要反復編寫,因此出現了標準庫的方式,對寄存器操作進行了封裝,操作相對簡單。隨著項目復雜度提升,又出現了封裝更厲害的一種庫HAL,這個需要借助STM32CubeMx工具來生成代碼。
? ? 簡單來說,STM32編碼有三種方式:
? ? 1、寄存器編碼。
? ? 2、標準庫操作編碼。
? ? 3、HAL庫操作編碼。
? ? 這三種方式各有優劣勢,寄存器編碼更偏向底層,方便我們熟悉單片機內部結構,開發效率相對較低,因為很多初始化,賦值操作需要反復編寫,使用HAL庫,可以減少大量代碼,提高了開發效率,但是不便于理解底層邏輯,需要非常熟練STM32單片機工作邏輯之后才好上手。
? ? 第一種寄存器編碼方式,構建項目可以直接新建一個普通項目,如下所示:
?
? ? 項目新建之后,什么也沒有,我們需要手動添加一個啟動文件和一個main.c文件到工程目錄:
?
? ? 我們在keil工具里面,通過添加現有項的方式將這兩個文件加到Source Group 1?中,如下所示:
? ? ?這個時候,編譯會報錯:
? ? 說是SystemInit符號未定義,其實是我們構建的項目代碼里面(main.c)中缺少SystemInit()方法,添加上就可以了。
? ? 這個項目只有兩個文件,一個是啟動文件,一個是主程序main.c。我們操作寄存器的代碼就在main.c中編寫,這里給出一個簡單的實現LED燈閃爍的示例代碼:
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE+ 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE+0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE+0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE+0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE+0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE+0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE+0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE+0x2000)#define GPIOA_ODR_Addr (GPIOA_BASE+12)
#define GPIOB_ODR_Addr (GPIOB_BASE+12)
#define GPIOC_ODR_Addr (GPIOC_BASE+12)
#define GPIOD_ODR_Addr (GPIOD_BASE+12)
#define GPIOE_ODR_Addr (GPIOE_BASE+12)
#define GPIOF_ODR_Addr (GPIOF_BASE+12)
#define GPIOG_ODR_Addr (GPIOG_BASE+12)#define BITBAND(addr,bitnum) ((addr & 0xF0000000) + 0x2000000 + ((addr&0xFFFFF)<<5) + (bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define LED0 MEM_ADDR(BITBAND(GPIOA_ODR_Addr, 5))typedef struct
{volatile unsigned int CR;volatile unsigned int CFGR;volatile unsigned int CIR;volatile unsigned int APB2RSTR;volatile unsigned int APB1RSTR;volatile unsigned int AHBENR;volatile unsigned int APB2ENR;volatile unsigned int APB1ENR;volatile unsigned int BDCR;volatile unsigned int CSR;
}RCC_TypeDef;#define RCC ((RCC_TypeDef*)0x40021000)typedef struct
{volatile unsigned int CRL;volatile unsigned int CRH;volatile unsigned int IDR;volatile unsigned int ODR;volatile unsigned int BSRR;volatile unsigned int BRR;volatile unsigned int LCKR;
}GPIO_TypeDef;#define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)void LEDInit(void)
{RCC->APB2ENR|=1<<2;GPIOA->CRL &= 0xFF0FFFFF;GPIOA->CRL |= 0x00300000;
}void Delay_ms(volatile unsigned int t)
{unsigned int i,j;for(j=0;j<t;j++)for(i=0;i<800;i++);
}void SystemInit(void)
{}int main(void)
{LEDInit();while(1){LED0 = 0;Delay_ms(500);LED0 = 1;Delay_ms(500);}
}
? ? 項目構建成功,不報錯就可以進行仿真,或者下載到單片機調試。
? ? 第二種方式,直接在Keil工具中構建STM32標準庫工程,不用額外拷貝標準庫文件到項目文件夾,然后添加現有項的方式加入組中,構建項目,選擇芯片系列之后,在彈出確認框這里可以選擇需要的庫:
? ? 這里勾選CMSIS->CORE,?Device->Startup , Device->StdPeriph Drivers->Framework,GPIO,RCC幾項。
? ? 自動生成的代碼結構如下所示:
? ? 這里面除了main.c是手動添加的,其余的都是通過keil自動生成的,如果你做過手動添加標準庫,那么就會很熟悉這里面的一些文件,stm32f10x_gpio.c,stm32f10x_rcc.c,startup_stm32f10x_ld.s,system_stm32f10x.c。
? ? 這里直接編譯會報錯:
? ? Error: L6218E: Undefined symbol assert_param (referred from misc.o).
? ? 這個問題是找不到assert_param這個函數,而這個函數是在stm32f10x_conf.h中,從上面的工程結構,我們看到代碼里面是有stm32f10x_conf.h這個頭文件的。解決辦法就是使用宏定義USE_STDPERIPH_DRIVER查找:
? ? 點擊魔法棒工具Options for?Targets,在彈出框中選擇c/c++,Define處輸入USE_STDPERIPH_DRIVER,Include Paths指定當前項目路徑下的RTE目錄即可。
? ? 最后可以編譯成功。
? ? 這里也給出一段讓LED閃爍的代碼。
#include "stm32f10x.h"void Delay()
{unsigned int i,j;for(i=0;i<1000;i++)for(j=0;j<1000;j++);
}void LED_Config()
{GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);
}int main(void)
{LED_Config();while(1){GPIO_SetBits(GPIOA, GPIO_Pin_5);Delay();GPIO_ResetBits(GPIOA, GPIO_Pin_5);Delay();}
}
? ? PA5口作為電平輸出, 時鐘使能與GPIO初始化,都調用標準庫中的方法。
? ? 第三種構建STM32工程的方法需要借助STM32CubeMx工具,這個是免費的,安裝之后,可以圖形化界面操作,如下所示:
? ? 1)打開工具之后,新建工程,來到選擇芯片界面:
? ? 這里在Commecial Part Number這里輸入STM32F103會自動補全C6A,選中右側面板中的一個芯片雙擊,
? ?2)進入配置界面。
? ? 展開System Core菜單,默認選中SYS,我們點擊,在中間模式這里選擇Debug: Serial Wire。
? ? 然后,選中RCC
? ? 設置HSE/LSE為Crystal/Ceramic Resonator。改變之后,右側芯片會有變化。
? ? 接著,還要將芯片上的PA5端口作為GPIO_Output,點擊PA5,就會出現菜單選項,直接選擇即可。?
? ?3)工程位置及編譯工具設置
? ? 設置工程名,工程位置,Toolchain/IDE選擇MDK-ARM,版本選擇V5。
? ? 4)代碼生成。點擊GENERATE CODE按鈕。
? ? 5)生成的代碼,可以直接用Keil打開,結構如下:
?
? ? 連main.c都寫好了,直接編譯也沒有問題。我們要實現LED閃爍,只需要在while(1)循環體中增加如下代碼:
while (1){/* USER CODE END WHILE */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);HAL_Delay(200);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);HAL_Delay(200);/* USER CODE BEGIN 3 */}
? ? 這種方式生成的代碼,不僅幫我們做了很多工作,甚至連編譯準備都做好了,我們看看Options for Targets的c/c++選項:
?
? ?宏定義查找設置了,不用我們手動設置,另外編譯生成hex文件,也同樣幫我們勾選好了。
? ?使用HAL庫,自己編寫的代碼很少,但是理解起來需要花時間,默認操作做了什么,這里面隱含了哪些操作。? ?