對於初學者而言,最簡單的是對晶元上的IO進行操作,我們學習ARM時候,第一個工程就是點亮LED,STM32F103ZET6通用輸入輸出介面(General-Purpose Inputs/Outputs),每個GPIO都可以由軟體配置成輸出(推免或開漏)、輸入(帶或不帶上拉或下拉)或復用的外設功能埠 ...
對於初學者而言,最簡單的是對晶元上的IO進行操作,我們學習ARM時候,第一個工程就是點亮LED,STM32F103ZET6通用輸入輸出介面(General-Purpose Inputs/Outputs),每個GPIO都可以由軟體配置成輸出(推免或開漏)、輸入(帶或不帶上拉或下拉)或復用的外設功能埠。多數GPIO引腳都與數字或模擬的復用外設共用。具體的細節請參考Datasheet。
回到MDK開發平臺,現在要在main.c中加入相關代碼了。代碼清單如下:
#include "stm32f10x_lib.h" int main() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB, ENABLE); //開啟外設時鐘 GPIOD->CRL = 0x33333333; //設置埠配置寄存器 GPIOB->CRL = 0x33333333; while(1) { GPIOD->ODR = 0xffffffbf; //設置埠輸出寄存器 for(i=0;i<1000000;i++); //延時 GPIOD->ODR = 0xffffffff7; for(i=0;i<1000000;i++); GPIOD->ODR = 0x00000000; GPIOB->ODR = 0xffffffff; for(i=0;i<1000000;i++); GPIOB->ODR = 0x00000000; } }
上述代碼中,#include "stm32f10x_lib.h"包含了開發stm32f10x系列晶元所需的基本頭文件,在進行程式編寫的時候,務必要包含此頭文件。
RCC_APB2PeriphClockCmd()函數是設置外設時鐘。ARM與C51單片機不同的是,不用外設的時候,如IO口、ADC、定時器等等,都是禁止時鐘的,以達到節能的目的,只有要用到的外設,才開啟它的時鐘。因此在需要用到GPIOB和GPIOD的時候,我們需要先開啟它的時鐘,具體用到的是函數庫裡面的函數:
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
其中,第一個參數需要指示要開啟什麼埠的時鐘,RCC_APB2Periph_GPIOx就是開啟GPIOx的時鐘,第二個參數需要指示是開啟還是關閉,ENABLE/DISABLE。
開啟外設時鐘之後,然後就開始對GPIO的配置寄存器進行設置了。具體設置方式參考《基於MDK的STM32處理器開發應用》一書中,“7.1 通用IO埠”。While迴圈裡面就是給GPIO的埠輸出寄存器賦值,由於我手上這款開發板三個LED燈分別接的是PG13、PG14和PG15,所以只要將G埠相應的位上置1就可以了。
編譯之後我們發現編譯器報錯,Undefined symbol RCC_APB2PeriphClockCmd,是因為我們使用了的RCC_APB2PeriphClockCmd()函數在頭文件中聲明瞭,卻沒有在C文件中定義,這個函數在Keil\ARM\RV31\LIB\ST\STM32F10x\stm32f10x_rcc.c中,將這個文件複製到工程的根目錄下,然後在屏幕左邊的Workspace中添加進來,就可以了。
其實在MDK的庫中,還定義了很多巨集,可以避免讓我們自己去查找相關資料來設置寄存器的各個位。
對於LED的亮滅可用以下代碼進行實現:
#include "stm32f10x.h" static u8 fac_us=0; //us延時倍乘數 static u16 fac_ms=0; //ms延時倍乘數,在ucos下代表每個節拍的ms數 void delay_init(void); void delay_ms(u16 nms); void LED_Init(void);//初始化 int main(void) { delay_init(); //初始化延時函數 LED_Init(); //初始化LED埠 while(1) { GPIO_ResetBits(GPIOG,GPIO_Pin_13); //亮 等同LED0=0; GPIO_SetBits(GPIOG,GPIO_Pin_14); //滅 等同LED1=1; GPIO_SetBits(GPIOG,GPIO_Pin_15); //滅 等同LED2=1; delay_ms(500); //延時500ms GPIO_SetBits(GPIOG,GPIO_Pin_13); GPIO_ResetBits(GPIOG,GPIO_Pin_14); GPIO_SetBits(GPIOG,GPIO_Pin_15); delay_ms(500); GPIO_SetBits(GPIOG,GPIO_Pin_13); GPIO_SetBits(GPIOG,GPIO_Pin_14); GPIO_ResetBits(GPIOG,GPIO_Pin_15); delay_ms(500); } } void LED_Init(void) //LED對應IO初始化 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE); //使能GPIO時種 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //LED0,LED1,LED2對應IO口 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推免輸出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO速度為50Mhz GPIO_Init(GPIOG, &GPIO_InitStructure); //初始化GPIO GPIO_SetBits(GPIOG,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //GPIOG13,G14,G15設置高,燈滅 void delay_init() //延時函數初始化 { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us=SystemCoreClock/8000000; fac_ms=(u16)fac_us*1000; //每個ms需要的systick時鐘數 } void delay_ms(u16 nms) //延時nms { u32 midtime; SysTick->LOAD=(u32)nms*fac_ms;//時間載入(SysTick->LOAD為24bit) //SysTick->LOAD為24位寄存器,所以最大延時為:nms<=0xffffff*8*1000/SYSCLK對72M條件下,nms<=1864ms SysTick->VAL =0x00; //清空計時器÷ SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //開始倒數 do { midtime=SysTick->CTRL; } while((midtime&0x01)&&!(midtime&(1<<16)));//等待時間到達 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //關閉計數器 SysTick->VAL =0X00; //清空計數器 }
由於我們使用了GPIO_InitTypeDef類型,所以我們需要找到它的定義,這個定義包含在“…\Keil\ARM\RV31\LIB\ST\STM32F10x\stm32f10x_gpio.c”中,將文件複製到工程根目錄下,然後再添加進入工程中,編譯才不會報錯。
在絕大多數C編譯器中,要求所有的變數聲明都在執行語句塊之前,也就是說如果需要定義的變數需要先在進入main函數一開始就全部定義好,如果執行了某一條語句之後再定義變數的話,就會報錯。
部分參考:http://blog.sina.com.cn/s/blog_49cb42490100robb.html
補充:
有三種方式可以控制LED亮滅:
1、通過位段操作實現IO口控制;
int main(void) { delay_init(); //初始化延時函數 LED_Init(); //初始化LED埠 while(1) { GPIO_bits_OUT(GPIOG,13,3,0x0006); delay_ms(500); GPIO_bits_OUT(GPIOG,13,3,0x0005); delay_ms(500); GPIO_bits_OUT(GPIOG,13,3,0x0003); delay_ms(500); } } /*以下:*GPIOx:對應的IO口,start_bit:並行輸出的起始值,bit_size:並行輸出的位數*/ void GPIO_bits_OUT(GPIO_TypeDef* GPIOx, u8 start_bit, u8 bit_size,u16 outdata) { u8 i=0; u16 bu1=0;u16 middata=1; if( bit_size>(16-start_bit) ) bit_size=16-start_bit; i=start_bit; if(i>0) { while(i--) { bu1+=middata; middata*=2;} } assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); GPIOx->ODR&=( ( (0xffff<<bit_size) <<start_bit ) |bu1 ); GPIOx->ODR|=(outdata<<start_bit); }
2、通過位帶操作實現IO口控制;
3、通過庫函數直接操作實現IO控制;