久久ER99热精品一区二区-久久精品99国产精品日本-久久精品免费一区二区三区-久久综合九色综合欧美狠狠

新聞中心

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

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

作者: 時間:2012-10-22 來源:網絡 收藏

  至此,按我的習慣,要翻開F的數據手冊,一下其端口了。下面是數據手冊中的一段話:
  -------------------------------------
  每個GPI/O端口有兩個32位配置寄存器(GPx_CRL,GPx_CRH),兩個32位數據寄存器(GPIOx_IDR,GPIOx_ODR),一個32位置位/復位寄存器(GPIOx_BSRR),一個16位復位寄存器(GPIOx_BRR)和一個32位鎖定寄存器(GPIOx_LCKR)。
  根據數據手冊中列出的每個I/O端口的特定硬件特征, GPIO端口的每個位可以由軟件分別配置成多種模式。
  ─ 輸入浮空
  ─ 輸入上拉
  ─ 輸入下拉
  ─ 模擬輸入
  ─ 開漏輸出
  ─ 推挽式輸出
  ─ 推挽式復用功能
  ─ 開漏復用功能
  ----------------------------------------------------
  當然,數據手冊上關于IO端口的描述是很多很多的,我也只是大概地了解了一下,真正要設計產品時,肯定還要細看。但至少,知道了IO端口復位后處于浮空狀態,也就是其電平狀態由外圍電路決定,這很重要,如果設計工業品的話,這是必須要確定的;知道了IO引腳可以兼容5V電源;知道了在什么地方可以找到這些引腳在庫中的定義而不必看著數據手冊去控制那些位;也知道了這些引腳的一些基本操作函數(連猜帶蒙帶測試應該可以搞定大部分功能),那么我心里基本就有底啦。
  最后用一段電子熒火蟲(也就是呼吸燈)的簡單作為結束。
  int main(void)
  { uint8_t Count;
  uint32_t DelayTim=“0x1000”; //基數
  uint32_t ChangTim=“0x2000”; //每次變化的量
  Init_All_Periph();
  while(1)
  {
  for(Count=0;Count《16;Count++) //漸亮
  {
  GPIO_SetBits(GPIOD, GPIO_Pin_8); //點亮燈
  Delay(DelayTim+Count*ChangTim);
  GPIO_ResetBits(GPIOD, GPIO_Pin_8); //熄滅燈
  Delay(DelayTim+(16-Count)*ChangTim);
  }
  for(Count=16;Count》0;Count--) //漸暗
  {
  GPIO_SetBits(GPIOD, GPIO_Pin_8); //點亮燈
  Delay(DelayTim+Count*ChangTim);
  GPIO_ResetBits(GPIOD, GPIO_Pin_8); //熄滅燈
  Delay(DelayTim+(16-Count)*ChangTim);
  }
  }
  }
  定時器初步
  接下來定時器。為什么開始定時器了呢?那個I/O還是剛剛開了頭啊,還有很多很多知識沒有掌握!不怕,方法論告訴我們:對事物的認識是螺旋式上升的,所以不要一桿子打到底,想著把I/O口的所有情況都掌握了再學下面的,那會是很困難的
  的定時器是什么樣子的,心里一點底也沒有,還是找個現成的例子來吧。在ST提供的庫里,有很多的例子
  第一個就是它了。
  
  把整個文件夾復制一份到自己的實驗文件夾中
  
  在Source文件夾中再建立名為APP的文件夾,將上圖中所有源文件全部復制到APP文件夾中。然后將庫所提供的CMSIS文件夾和 F10x_StdPeriph_Driver文件夾復制到Source文件夾中。然后按照前面的方法建立項目。
  
  這是建好的項目的結構。
  下面開始研究,首先看附帶的readme.txt文件,了解到該例子的大體用途是驗證Tim2的Output Compare Timing mode的。于是打開PDF文件,直接翻到下面的位置:
  -----------------------------------------------------------------------------------------

 13.3.8 輸出比較模式
  此項功能是用來控制一個輸出波形或者指示何時一段給定的的時間已經到時。
  當計數器與捕獲/比較寄存器的內容相同時,輸出比較功能做如下操作:
  ● 將輸出比較模式(TIMx_CCMRx寄存器中的OCxM位)和輸出極性(TIMx_CCER寄存器中的CCxP位)定義的值輸出到對應的管腳上。在比較匹配時,輸出管腳可以保持它的電平
  (OCxM=000)、被設置成有效電平(OCxM=001)、被設置成無有效電平(OCxM=010)或進行翻轉(OCxM=011)。
  ● 設置中斷狀態寄存器中的標志位(TIMx_SR寄存器中的CCxIF位)。
  ● 若設置了相應的中斷屏蔽(TIMx_DIER寄存器中的CCXIE位),則產生一個中斷。
  ● 若設置了相應的使能位(TIMx_DIER寄存器中的CCxDE位,TIMx_CR2寄存器中的CCDS位選擇DMA請求功能),則產生一個DMA請求。
  TIMx_CCMRx中的OCxPE位選擇TIMx_CCRx寄存器是否需要使用預裝載寄存器。
  在輸出比較模式下,更新事件UEV對OCxREF和OCx輸出沒有影響。
  同步的精度可以達到計數器的一個計數周期。輸出比較模式(在單脈沖模式下)也能用來輸出一個單脈沖。
  輸出比較模式的配置步驟:
  1. 選擇計數器時鐘(內部,外部,預分頻器)
  2. 將相應的數據寫入TIMx_ARR和TIMx_CCRx寄存器中
  3. 如果要產生一個中斷請求和/或一個DMA請求,設置CCxIE位和/或CCxDE位。
  4. 選擇輸出模式,例如:必須設置OCxM=’011’、OCxPE=’0’、CCxP=’0’和CCxE=’1’,當計數器CNT與CCRx匹配時翻轉OCx的輸出管腳,CCRx預裝載未用,開啟OCx輸出且高電平有效。
  5. 設置TIMx_CR1寄存器的CEN位啟動計數器--------------------------------------------------------------------------------
  說得并不復雜,但是要弄清楚也絕非易事,況且main.c中一系列的符號究竟是什么意思也不是那么容易搞清楚的。這些東西搞不清,將來自己編程時,就算想要依葫蘆畫瓢都難。怎么辦呢?可能并沒有多少偷巧的辦法,只能是一步一步地摸索吧。
  進入調試,打開Peripherals-》Timers-》Tim2,出現下面的窗口。
  
  然后單步執行,并觀察界面的變化,并將這些符號作為線索,在PDF文件,stmf10x_tim.c等文件中搜索,了解相關符號的含義。
  如下圖是執行到:while(1)前,也就是所有設置完成后的圖。
  

沿著這些線索一路追蹤,邊看源程序,邊找數據手冊,邊找相關的頭文件,其他源程序,總算大體有了個明白,下面將main.c中的部分代碼作為注釋,也算是做點記錄。
  /* Includes ------------------------------------------------------------------*/
  #include “stm32f10x.h”
  //這個頭文件需要根據所選擇的芯片進行更改
  /* Private variables ---------------------------------------------------------*/
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_OCInitTypeDef TIM_OCInitStructure; //定義兩個結構型變量
  __IO uint16_t CCR1_Val = 49152;
  __IO uint16_t CCR2_Val = 32768;
  __IO uint16_t CCR3_Val = 16384;
  __IO uint16_t CCR4_Val = 8192; //
  ErrorStatus HSEStartUpStatus;
  /* Private function prototypes -----------------------------------------------*/
  void RCC_Configuration(void);
  void GPIO_Configuration(void);
  void NVIC_Configuration(void);
  int main(void)
  {
  /* System Clocks Configuration */
  RCC_Configuration();
  /* NVIC Configuration */
  NVIC_Configuration();
  /* GPIO Configuration */
  GPIO_Configuration();
  /* ---------------------------------------------------------------
  TIM2 Configuration: Output Compare Timing Mode:
  TIM2CLK = 36 MHz, Prescaler = 4, TIM2 counter clock = 7.2 MHz
  CC1 update rate = TIM2 counter clock / CCR1_Val = 146.48 Hz
  CC2 update rate = TIM2 counter clock / CCR2_Val = 219.7 Hz
  CC3 update rate = TIM2 counter clock / CCR3_Val = 439.4 Hz
  CC4 update rate = TIM2 counter clock / CCR4_Val = 878.9 Hz
  --------------------------------------------------------------- */
  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 65535; //它對應TIM2_ARR
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  /* 它對應TIM2_PSC,相關代碼如下(stm32f10x_tim.c中):
  TIMx-》PSC = TIM_TimeBaseInitStruct-》TIM_Prescaler;
  但很奇怪,這里令其為0,然后再在下面設置為4??為何??
  */
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0200;
  //ClockDivision是對CKD位進行設置的,但是這個必須要自己給其賦正確的值
  /*在stm32f10_tim.c文件中是這樣操作的
  TIMx-》CR1 |= (uint32_t)TIM_TimeBaseInitStruct-》TIM_ClockDivision |
  TIM_TimeBaseInitStruct-》TIM_CounterMode;
  */
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  /*計數模式,st32f10x_tim.h中定義,其他可用的符號還有:
  TIM_CounterMode_Up,TIM_CounterMode_Down,TIM_CounterMode_CenterAligned1
  TIM_CounterMode_CenterAligned2,TIM_CounterMode_CenterAligned3
  */
  TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure);
  /* Prescaler configuration */
  TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate);
  /*stm32f10x_tim.c中相關代碼:
  TIMx-》PSC = Prescaler;
  */
  /* Output Compare Timing Mode configuration: Channel1 *///輸出比較模式
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //預置值
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //對CCER中CCxP的操作
  TIM_OC1Init(TIM2, TIM_OCInitStructure);
  /*對捕獲/比較使能寄存器(TIMx_CCER)進行操作,置CC1E為1 */
  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
  /* Output Compare Timing Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
  //這個是將CCR2_Val的值送到TMI2_CCR2中
  TIM_OC2Init(TIM2, TIM_OCInitStructure);
  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);
  /* Output Compare Timing Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
  TIM_OC3Init(TIM2, TIM_OCInitStructure);
  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
  /*
  */
  /* Output Compare Timing Mode configuration: Channel4 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
  TIM_OC4Init(TIM2, TIM_OCInitStructure);
  TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable);
  /* TIM IT enable */
  TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
  對DIER寄存器操作,中斷允許配置,相關代碼如下:
  
  而DIER寄存器如下:
  
  /* TIM2 enable counter */
  TIM_Cmd(TIM2, ENABLE);
  //開啟定時器的運行
  while (1);
  }
 解讀:
  (1) 時鐘來源 CK_INT,設置的方法是讓TIMx_SMCR中的SMS[2:0]=000,最后通過TIM_Cmd(TIM2, ENABLE);函數將TIMx_CR1中的CEN置1,開啟定時器的運行;
  (2) 接下來設置定時器的基本參數
  (3) 然后是設置定時器的各個通道
  (4) 最后開啟定時器運行
  最后,將這個例子稍加修改,令其運行在我的實驗板上。
  需要修改的僅是將其原來的輸出從GPIOC的第6~第9腳變為GPIOD的第8~第11腳。為此,需要改的地方有:
  
  將藍色框內的GPIOC改為GPIOD。這個是最先寫的,但實際上一開始根本沒注意到這個地方,沒有改成GPIOD,結果一仿真,不正確,再一細查,原來端口還要配置時鐘,改過來就對了。瞧,這不驗證了上面的說法“認識事物是螺旋式上升的”學了定時器,對于I/O口和時鐘又有了更進一步的理解了。
  
  將紅色框內的GPIO分別改成8,9,10,11,將藍色框內的GPIOC改為GPIOD。
  最后,到stm32f10x_it.c中,修改相應的輸出
  
  參考上圖紅色框內,將GPIOC改為GPIOD,將6腳改為8腳,其他部分類推。
  修改好后運行,所有燈亮了,看不出效果,于是又將預分頻系數由4改為64,這樣一來,LED開始閃爍了。

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

單片機相關文章:單片機教程


單片機相關文章:單片機視頻教程


單片機相關文章:單片機工作原理


分頻器相關文章:分頻器原理
塵埃粒子計數器相關文章:塵埃粒子計數器原理
晶振相關文章:晶振原理
鎖相環相關文章:鎖相環原理

上一頁 1 2 下一頁

評論


相關推薦

技術專區

關閉