我的第一個項目是STM32嵌入式系統設計,包括了硬件部分和軟件部分。項目與公共衛生學院合作,主要內容是關于藥劑檢測。
之前做過用51單片機開發板讀取溫度濕度的小項目,主要的任務是代碼的編寫,相比于51單片機這種入門級別的MCU,STM32在各方面性能都領先51單片機,但是使用起來也復雜不少,光是選擇用庫函數法或是寄存器法編程就讓人糾結。
我主要通過視頻+相關文檔的方法來學習STM32。
視頻地址:【正點原子】STM32開發板實驗教程,視頻講的很細,電路原理圖也都有講,不過涉及到原理乏味而又高深。
相對應的實驗配套文檔:正點原子戰艦版資料
其中個人認為最有用的是:
①軟件資料下的軟件壓縮包,有MDK5的安裝包和各種實用軟件,比如程序下載軟件、串口軟件
②STM32F1開發指南,包括庫函數版和寄存器版。
③STM32參考資料文件夾下的STM32中文參考手冊
④STM32F103ZET6
⑤程序源碼,包括庫函數版和寄存器版。
STM32快速簡介
我總結了下我對STM32的理解:
STM32是ST公司生產的MCU,32位,內核是ARM公司的Cortex內核。Cortex內核有A,R,M三個系列,STM32主要是Cortex-M系列。Cortex-M3內核在各方面都領先于ARM7內核,成本也更低。
配置程序的模板框架其實挺復雜的,但其實不需要自己一步一步的建立,直接套用程序源碼中的模板就好了。現成的有STM32F103系列和STM32F407系列的模板。
為了方便開發,我最后選擇的是庫函數法。本文以串口通信作為例子,盡量詳盡的給出模板,中間必須要用到的頭函數定義和GPIO函數也進行了一定的介紹。
頭文件設置
#ifndef __XXX_H
#define __XXX_H
#include "XXXXXX.h"
void XXX//各種函數聲明
#define LED0 PAout(1)//各種宏定義,PAout(1)這樣的位帶操作在sys.h中定義
#endif
GPIO的設置,包括輸入輸出
(1)初始化
GPIO_InitTypeDef GPIO_InitStructure; //定義名稱為GPIO_InitStructure的結構體
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz,數值感覺不太重要
GPIO_Init(GPIOB, &GPIO_InitStructure); //根據設定參數初始化GPIOB.5
其中GPIO_InitTypeDef是一個結構體,主要包括GPIO的引腳、速度、模式三個參數。
typedef struct
{
uint16_t GPIO_Pin; //GPIO_Pin_0到GPIO_Pin_15
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
GPIO_Speed的參數規范為:
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
輸出模式必須配置,輸入模式無須配置。設置端口的翻轉速度級別為50MHz,這種級別時端口能輸出頻率很高的信號,但要求外設的容性負載很小。另外還有2MHz和10MHz級別的,能驅動容性負載較大的外設。
GPIO_Mode的參數規范為:
typedef enum
{ GPIO_Mode_AIN = 0x0, //模擬輸入
GPIO_Mode_IN_FLOATING = 0x04, //浮空輸入
GPIO_Mode_IPD = 0x28, //下拉輸入
GPIO_Mode_IPU = 0x48, //上拉輸入
GPIO_Mode_Out_OD = 0x14, //開漏輸出
GPIO_Mode_Out_PP = 0x10, //通用推挽輸出
GPIO_Mode_AF_OD = 0x1C, //復用開漏輸出
GPIO_Mode_AF_PP = 0x18 //復用推挽輸出
}GPIOMode_TypeDef;
各種輸出輸入模式應用場合:
http://www.openedv.com/posts/list/21980.htm
(2)信號輸出和檢測輸入
有相應的庫函數
GPIO_SetBits(GPIOE,GPIO_Pin_5); //LED1對應引腳GPIOE.5拉高
GPIO_ResetBits(GPIOB,GPIO_Pin_5); //LED0對應引腳GPIOB.5拉低
GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4);//讀取GPIOE.4的輸入
用位帶操作法比較方便
PAout(1) = 1;
PBin(2);
按鍵設置(以低電平觸發為例)
其中u8是無符號數字8位的意思,進行過宏定義。
//mode=0時,不支持連按;mode=1時,支持連按
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按鍵按松開標志
if(mode)key_up=1; //支持連按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);//去抖動
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(WK_UP==1)return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0;// 無按鍵按下
}
串口通信
串口配置的一般步驟(以USART1為例,PA9為TX發送端,PA10為RX接收端為例):
①串口時鐘使能,GPIO時鐘使能:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
其他串口可能是RCC_APB1PeriphClockCmd();
②串口復位(可不寫):
USART_DeInit(USART1);
③GPIO端口模式設置:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
④串口參數初始化:
USART_InitTypeDef USART_InittStructure;
USART_InittStructure.USART_BaudRate = 115200; //波特率
USART_InittStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流設置
USART_InittStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //接收發送模式
USART_InittStructure.USART_Parity = USART_Parity_No; //奇偶校驗位
USART_InittStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InittStructure.USART_WordLength = USART_WordLength_8b; //字長
USART_Init(USART1, &USART_InittStructure);
⑤開啟中斷并且初始化NVIC(如果需要開啟中斷才需要這個步驟)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分組2,放到主函數main的開頭位置
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //設置中斷類型,USART_IT_RXNE表示接收緩沖區非空
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中斷,在stm32F10x.h中有定義
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //搶占優先級為1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //響應優先級為2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
⑥串口使能:
USART_Cmd(USART1, ENABLE);
⑦編寫中斷處理函數:
//會和system文件夾里的usart.h有沖突,刪掉usart.h。
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
}
}
⑧串口數據收發操作:
USART_SendData(USART1, uint16_t Data);
USART_ReceiveData(USART1);
串口通信需要的軟硬件設置:
①安裝CH340驅動(在軟件目錄下),讓電腦USB口能變成串口
②開發板USB口選擇USB_232(如圖)
③PA9、PA10通過跳線帽分別與RXD、TXD相連(如圖)
利用串口輸入指令控制程序
int main(void)
{
u16 len;
u8 flag = 0;
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級
uart_init(115200); //串口初始化為115200
LED_Init(); //LED端口初始化
KEY_Init(); //初始化與按鍵連接的硬件接口
MCP9808_init(); //溫度傳感器初始化
while(1)
{
if(USART_RX_STA&0x8000) //第15位標志位為1時表示接收完成
{
len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度,第0位~第13位
if(USART_RX_BUF[len-1]=='E')//指令以E(END)結束
{
if(USART_RX_BUF[0]=='R'&USART_RX_BUF[1]=='T'&USART_RX_BUF[2]==' '&len==4) //指令為RT E
{
printf("溫度為%.2f℃\r\n",MCP9808_ReadTP()); //顯示溫度
flag = 0;
}
else
flag = 1;
}
else
flag = 1;
if(flag==1)
{
flag =0;
printf("錯誤\r\n");
}
else
printf("代碼已執行\r\n");
USART_RX_STA = 0;
}
}
}
可以根據需求再增加指令。
關于串口1下載程序:
orient/strip%7CimageView2/2/w/1240)
BOOT0=1,BOOT1=0,如果下載一直識別不出來,嘗試按一按RST鍵。
下載完成后,由于軟件勾上了編成后執行,程序會開始執行。
若需要按RST鍵復位程序,需要再把BOOT0拉低。否則按RST鍵程序不會執行。