<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > 工程師STM32單片機學(xué)習基礎手記(2):從勉強看懂一行程序到IO口研究

工程師STM32單片機學(xué)習基礎手記(2):從勉強看懂一行程序到IO口研究

作者: 時(shí)間:2012-10-22 來(lái)源:網(wǎng)絡(luò ) 收藏

 強看懂

  繼續中,先把開(kāi)發(fā)板自帶一個(gè)例子做了些精簡(jiǎn),以免看得嚇人。。。。
  
  就是這個(gè),讓PORTD上接的4個(gè)LED分別點(diǎn)亮。
  開(kāi)始代碼
  int main(void)
  {
  Init_All_Periph();
  。。.。。.
  看到這,開(kāi)始跟蹤,于是又看到了下面的內容
  void Init_All_Periph(void)
  {
  RCC_Configuration();
  。。.。。.
  繼續跟蹤
  void RCC_Configuration(void)
  {
  SystemInit();
  。。.。。.
  這行代碼在system_stm32f10x.c中找到了。
  void SystemInit (void)
  {
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSN bit */
  RCC-》CR |= (uint32_t)0x00000001;
  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
  #ifndef F10X_CL
  RCC-》CFGR = (uint32_t)0xF8FF0000;
  #else
  RCC-》CFGR = (uint32_t)0xF0FF0000;
  #endif /* F10X_CL */
  /* Reset HSEON, CSSON and PLLON bits */
  RCC-》CR = (uint32_t)0xFEF6FFFF;
  /* Reset HSEBYP bit */
  RCC-》CR = (uint32_t)0xFFFBFFFF;
  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC-》CFGR = (uint32_t)0xFF80FFFF;
  #ifndef F10X_CL
  /* Disable all interrupts and clear pending bits */
  RCC-》CIR = 0x009F0000;
  #else
  /* Reset PLL2ON and PLL3ON bits */
  RCC-》CR = (uint32_t)0xEBFFFFFF;
  /* Disable all interrupts and clear pending bits */
  RCC-》CIR = 0x00FF0000;
  /* Reset CFGR2 register */
  RCC-》CFGR2 = 0x00000000;
  #endif /* STM32F10X_CL */
  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();
  }
  這一長(cháng)串的又是什么,如何來(lái)用呢?看來(lái),偷懶是不成的了,只能回過(guò)頭去STM32的時(shí)鐘構成了。
  相當的復雜。

本文引用地址:http://dyxdggzs.com/article/170756.htm

 系統的時(shí)鐘可以有3個(gè)來(lái)源:內部時(shí)鐘HSI,外部時(shí)鐘HSE,或者PLL(鎖相環(huán)模塊)的輸出。它們由RCC_CFGR寄存器中的SW來(lái)選擇。
  SW(1:0):系統時(shí)鐘切換
  由軟件置’1’或清’0’來(lái)選擇系統時(shí)鐘源。 在從停止或待機模式中返回時(shí)或直接或間接作為系統時(shí)鐘的HSE出現故障時(shí),由硬件強制選擇HSI作為系統時(shí)鐘(如果時(shí)鐘安全系統已經(jīng)啟動(dòng))
  00:HSI作為系統時(shí)鐘;
  01:HSE作為系統時(shí)鐘;
  10:PLL輸出作為系統時(shí)鐘;
  11:不可用。
  ////////////////////////////////////////////////////////////////////
  PLL的輸出直接送到USB模塊,經(jīng)過(guò)適當的分頻后得到48M的頻率供USB模塊使用。
  系統時(shí)鐘的一路被直接送到I2S模塊;另一路經(jīng)過(guò)AHB分頻后送出,送往各個(gè)系統,其中直接送往SDI,FMSC,AHB總線(xiàn);8分頻后作為系統定時(shí)器時(shí)鐘;經(jīng)過(guò)APB1分頻分別控制PLK1、定時(shí)器TIM2~TIM7;經(jīng)過(guò)APB2分頻分別控制PLK2、定時(shí)器TIM1~TIM8、再經(jīng)分頻控制ADC;
  由此可知,STM32F10x芯片的時(shí)鐘比之于51、AVR、PIC等8位機要復雜復多,因此,我們立足于對著(zhù)芯片手冊來(lái)解讀,力求知道這些代碼如何使用,為何這么樣使用,如果自己要改,可以修改哪些部分,以便自己使用時(shí)可以得心應手。
  單步執行,看一看哪些代碼被執行了。
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSN bit */
  RCC-》CR |= (uint32_t)0x00000001;
  120S52109-1.jpg
  這是RCC_CR寄存器,由圖可見(jiàn),HSN是其bit 0位。
  HSION:內部高速時(shí)鐘使能
  由軟件置’1’或清零。
  當從待機和停止模式返回或用作系統時(shí)鐘的外部4-25MHz時(shí)鐘發(fā)生故障時(shí),該位由硬件置’1’來(lái)啟動(dòng)內部8MHz的RC振蕩器。當內部8MHz時(shí)鐘被直接或間接地用作或被選擇將要作為系統時(shí)鐘時(shí),該位不能被清零。
  0:內部8MHz時(shí)鐘關(guān)閉;
  1:內部8MHz時(shí)鐘開(kāi)啟。
  ///////////////////////////////////////////////////////////////////////
  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
  #ifndef STM32F10X_CL
  RCC-》CFGR = (uint32_t)0xF8FF0000;
  點(diǎn)擊看大圖
  這是RCC_CFGR寄存器
  該行程序清零了MC0[2:0]這三位,和ADCPRE[1:0],ppre2[2:0],PPRE1[2:0],HPRE[3:0],SWS[1:0]和SW[1:0]這16位。
  /*
  MCO: 微控制器時(shí)鐘輸出,由軟件置’1’或清零。
  0xx:沒(méi)有時(shí)鐘輸出;
  100:系統時(shí)鐘(SYSCLK)輸出;
  101:內部8MHz的RC振蕩器時(shí)鐘輸出;
  110:外部4-25MHz振蕩器時(shí)鐘輸出;
  111:PLL時(shí)鐘2分頻后輸出。
  */
  /* Reset HSEON, CSSON and PLLON bits */
  RCC-》CR = (uint32_t)0xFEF6FFFF;
  清零了PLLON,HSEBYP,HSERDY這3位。
  /* Reset HSEBYP bit */
  RCC-》CR = (uint32_t)0xFFFBFFFF;
  清零了HSEBYP位 ///???為什么不一次寫(xiě)??
  HSEBYP:外部高速時(shí)鐘旁路,在調試模式下由軟件置’1’或清零來(lái)旁路外部晶體振蕩器。只有在外部4-25MHz振蕩器關(guān)閉的情況下,才能寫(xiě)入該位。
  0:外部4-25MHz振蕩器沒(méi)有旁路;
  1:外部4-25MHz外部晶體振蕩器被旁路。
  所以要先清HSEON位,再清該位。
  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC-》CFGR = (uint32_t)0xFF80FFFF;
  清零了:USBPRE,PLLMUL,PLLXTPR,PLLSRC共7位
  /* Disable all interrupts and clear pending bits */
  RCC-》CIR = 0x009F0000;
  ////這個(gè)暫不解讀
  SetSysClock();
 跟蹤進(jìn)入該函數,可見(jiàn)一連串的條件編譯:


  單步運行,執行的是:
  #elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
  為何執行該行呢,找到SYSCLK_PREQ_**的相關(guān)定義,如下圖所示。
  
  這樣就得到了我們所要的一個(gè)結論:如果要更改系統工作頻率,只需要在這里更改就可以了。
  可以繼續跟蹤進(jìn)入這個(gè)函數來(lái)觀(guān)察如何將工作頻率設定為72MHz的。
  static void SetSysClockTo72(void)
  {
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
  /* Enable HSE */
  RCC-》CR |= ((uint32_t)RCC_CR_HSEON);
  //開(kāi)啟HSE
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
  HSEStatus = RCC-》CR RCC_CR_HSERDY;
  StartUpCounter++;
  } while((HSEStatus == 0) (StartUpCounter != HSEStartUp_TimeOut));
  //等待HSE確實(shí)可用,這有個(gè)標志,即RCC_CR寄存器中的HSERDY位(bit 17),這個(gè)等待不會(huì )無(wú)限長(cháng),有個(gè)超時(shí)策略,即每循環(huán)一次計數器加1,如果計數的次數超過(guò)HSEStartUp_TimeOut,就退出循環(huán),而這個(gè)HSEStartUp_TimeOut在stm32f10x.h中定義,
  #define HSEStartUp_TimeOut ((uint16_t)0x0500) /*!《 Time out for HSE start up */
  ///////////////////////////////////////////////////////////////////////////////////////////////
  if ((RCC-》CR RCC_CR_HSERDY) != RESET)
  {
  HSEStatus = (uint32_t)0x01;
  }
  else
  {
  HSEStatus = (uint32_t)0x00;
  }
  ///再次判斷HSERDY標志位,并據此給HSEStatus變量賦值。
  if (HSEStatus == (uint32_t)0x01)
  {
  /* Enable Prefetch Buffer */
  FLASH-》ACR |= FLASH_ACR_PRFTBE;
  /* Flash 2 wait state */
  FLASH-》ACR = (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
  FLASH-》ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
  /* HCLK = SYSCLK */
  RCC-》CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  //找到定義: #define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000) /*!《 SYSCLK not divided */
  /* PCLK2 = HCLK */
  RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  //找到定義:#define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000) /*!《 HCLK not divided */
  /* PCLK1 = HCLK */
  RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
  //找到定義:#define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400) /*!《 HCLK divided by 2 */
  #ifdef STM32F10X_CL
  ……
  #else
  /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
  RCC-》CFGR = (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
  RCC_CFGR_PLLMULL));
  RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
  #endif /* STM32F10X_CL */
  //以上是設定PLL的倍頻系數為9,也就是說(shuō),這個(gè)72M是在外部晶振為8M時(shí)得到的。
  /* Enable PLL */
  RCC-》CR |= RCC_CR_PLLON;
  /* Wait till PLL is ready */
  while((RCC-》CR RCC_CR_PLLRDY) == 0)
  {
  }
  /* Select PLL as system clock source */
  RCC-》CFGR = (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC-》CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
  /* Wait till PLL is used as system clock source */
  while ((RCC-》CFGR (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
  {
  }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
  configuration. User can add here some code to deal with this error */
  /* Go to infinite loop */
  while (1)
  {
  }
  }
  }

  至此,我們可以歸納幾條:
 ?。?) 時(shí)鐘源有3個(gè)
 ?。?) 開(kāi)機時(shí)默認是HSI起作用,可以配置為所要求的任意一個(gè)時(shí)鐘
 ?。?) 配置時(shí)必須按一定的順序來(lái)打開(kāi)或都關(guān)閉一些位,并且各時(shí)鐘起作用有一定的時(shí)間,因此要利用芯片內部的標志位來(lái)判斷是否可以執行下一步。
 ?。?) 如果外部時(shí)鐘、PLL輸出失效,系統可以自動(dòng)回復到HSI(開(kāi)啟時(shí)鐘安全系統)
 ?。?) HSI的頻率準確度可以達到+/- 1%,如果有必要時(shí),還可以用程序來(lái)調整這個(gè)頻率,可調的范圍大致在200KHz左右。
  最后讓我們來(lái)感受一下勞動(dòng)的果實(shí)吧--試著(zhù)改改頻率看有何反應。
  為查看更改后的效果,先記錄更改前的數據。將調試切換到仿真,在第一條:
  Delay(0xAFFFF);
  指令執行前后,分別記錄下Status和Sec
  Status:2507 3606995
  Sec:0.00022749 0.05028982
  將振蕩頻率更改為36MHz,即
  。。.
  #define SYSCLK_FREQ_36MHz 36000000 //去掉該行的注釋
  /* #define SYSCLK_FREQ_48MHz 48000000 */
  /* #define SYSCLK_FREQ_56MHz 56000000 */
  /*#define SYSCLK_FREQ_72MHz 72000000*/ //將該行加上注釋
  再次運行,結果如下:
  Status:2506 3606994
  Sec:0.00008478 0.10036276
  基本上是延時(shí)時(shí)間長(cháng)了一倍。改成硬件仿真,將代碼寫(xiě)入板子,可以看到LED閃爍的頻率明顯變慢了。

IO

  前面的例子研究了時(shí)鐘,接下來(lái)就來(lái)了解一下引腳的情況
  Main.c中,有關(guān)I/O口的配置代碼如下:
  void GPIO_Configuration(void)
  {
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure IO connected to LD1, LD2, LD3 and LD4 leds *********************/
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOD, GPIO_InitStructure);
  這幾行代碼是將GPIOD的第8,9,10和11引腳配置成輸出,并且還可以設定輸出引腳的速度(驅動(dòng)能力?),這里設定為 50MHz,這應該是常用的,還有可以設置為2MHz的。那么如何將引腳設置成輸入呢?查看電路原理圖,GPIOD.0~GPIO.4是接一個(gè)搖桿的5個(gè)按鈕的,因此,下面嘗試著(zhù)將它們設置成為輸入端。
  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOD, GPIO_InitStructure);
  第1行和第3行完全是照抄,第2行那個(gè)GPIO_Mode_IN_FLOATING是在stm32f10x_gpio.h中找到的。
  
  當然是因為這里還有GPIO_Mode_Out_PP,所以猜測應該是它了。至于還有其他那么多的符號就不管了。
  定義完成,編譯完全通過(guò),那就接下來(lái)準備完成下面的代碼了。
  int main(void)
  {
  Init_All_Periph();
  while(1)
  { if( GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_0)) //1
  { GPIO_ResetBits(GPIOD, GPIO_Pin_8);
  }
  else
  { /* Turn on LD1 */
  GPIO_SetBits(GPIOD, GPIO_Pin_8);
  /* Insert delay */
  }
  。。.。。.
  標號為1的行顯然其作用是判斷GPIOD.0引腳是0還是1。這個(gè)函數是在stm32f10x_gpio.c中找到的。
  uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  {
  uint8_t bitstatus = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
  if ((GPIOx-》IDR GPIO_Pin) != (uint32_t)Bit_RESET)
  {
  bitstatus = (uint8_t)Bit_SET;
  }
  else
  {
  bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
  }
  雖然程序還有很多符號看不懂(沒(méi)有去查),但憑感覺(jué)它應該是對某一個(gè)引腳的狀態(tài)進(jìn)行判斷,因為這個(gè)函數的類(lèi)型是uint8_t,估計stm32沒(méi)有bit型函數(需要驗證),所以就用了uint8_t型了),如果是讀的端口的值,應該用uint16_t型。這一點(diǎn)在下面也可以得到部分的驗證:
  uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
  uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
  這些函數是讀引腳及輸出寄存器的數據的。

 再次編譯,也是順利通過(guò),依法炮制,將其他三個(gè)引腳輸入控制LED的代碼也寫(xiě)上,為保險起見(jiàn),先用軟件仿真,免得反復擦寫(xiě)FLASH(順便說(shuō)一句,目前還沒(méi)有搞定將代碼寫(xiě)入RAM及從RAM中執行)
  點(diǎn)擊看大圖
  進(jìn)入仿真后打開(kāi)外圍部件接口,單步執行,果然如同設想那樣運作了,單擊Pins 0后面的勾,再次運行,果然PIN8后面的勾沒(méi)了。做到這里,就感覺(jué)到用keil的好處了,這塊熟啊,幾乎沒(méi)有花時(shí)間在上面,一用就成了。

單片機相關(guān)文章:單片機教程


單片機相關(guān)文章:單片機視頻教程


單片機相關(guān)文章:單片機工作原理


分頻器相關(guān)文章:分頻器原理
塵埃粒子計數器相關(guān)文章:塵埃粒子計數器原理
晶振相關(guān)文章:晶振原理
鎖相環(huán)相關(guān)文章:鎖相環(huán)原理

上一頁(yè) 1 2 下一頁(yè)

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>