AMR學習筆記之一

2020-08-10 01:33:10

STM32F103最小系統實現智慧控制(暫存器實現)

一、硬體準備

1、學習前準備:整個專案使用C語言開發,所以,需要有良好的C語言基礎,這裏我就簡單說明一下,C語言中,通用鏈表,函數指針,檔案分類,這三點做好,在開發過程中會輕鬆很多。
2、硬體準備:網上購置stm32f103ze學習版,帶有電阻屏的(電容屏貴,沒錢),還有程式碼燒寫器(jlink),還有語音解碼模組,SD卡,DHT11,MP3播放模組,硬體連線圖如下
在这里插入图片描述

3、軟體準備:keil 4

二、實現功能

1、實現功能:時鐘,貪吃蛇,音樂播放器,溫溼度動態顯示,記事本,LED燈控制(上述只能控制均可用語音控制實現)

關鍵程式碼剖析

1、時鐘:晶片的最小單位都是暫存器,如何實現定時器,就如何實現時鐘,對於時間的顯示,只需要計算一個定時器,然後顯示數位就好,這裏使RTC時鐘

void TimeCount_Install(u8 Time_nu,int Count_time,Tm_pHandle PHandle)
{
 switch(Time_nu)
 {
  case TIM2_IRQn:
  case TIM3_IRQn:
  case TIM4_IRQn:
   RCC->APB1ENR |= (1<<(Time_nu-28));
   RCC->CFGR &= ~(7<<8);  //清空
   RCC->CFGR |= (4<<8);   //2分頻
   switch(Time_nu)
   {
    case TIM2_IRQn:
     TIM2->ARR = (Count_time-1);  //確定重灌載暫存器的值 72MHz=7200*10000,1s計數器產生中斷
     TIM2->PSC = (7200-1);   //設定預分頻器(TIMx_PSC)
     TIM2->DIER |= (1<<0);   //允許跟新中斷
     break;
    case TIM3_IRQn:
     TIM3->ARR = (Count_time-1);  //確定重灌載暫存器的值  72MHz=7200*10000,1s產生中斷
     TIM3->PSC = (7200-1);   //設定預分頻器(TIMx_PSC)
     TIM3->DIER |= (1<<0);   //允許跟新中斷
     break;
    case TIM4_IRQn:
     TIM4->ARR = (Count_time-1);    
     TIM4->PSC = (7200-1);   
     TIM4->DIER |= (1<<0);   
     break;
   }
   break;
  case TIM5_IRQn:
   RCC->APB1ENR |= (1<<3);
   RCC->CFGR &= ~(7<<8);  
   RCC->CFGR |= (4<<8);   
   TIM5->ARR = (Count_time-1);  
   TIM5->PSC = (7200-1);   
   TIM5->DIER |= (1<<0);   
   break;
 }
 IRQ_Install(Time_nu,PHandle);
 TimeCount_ON(Time_nu);                             //計數器開啓,硬定時器
}

需要說明的是,硬定時器和軟定時器,通俗來說,我們在做開發的時候,需要用到很多定時器,但是,對於M3系列的晶片來說,條件有限,所以,我們稱,原本的定時器爲硬定時器,所謂軟定時器,就是,在硬定時器中,再次計數 ,假設硬定時器是1秒,則在裏面技術時,那麼我們就可以得到10秒鐘的軟定時器,但這裏,爲了精確,一般硬定時器都是毫秒,微秒級。(個人理解)
2、中斷封裝
總共有60個外部中斷,每一箇中斷都有一箇中斷動作函數,所以,我們在使用中斷的時候,就可以在對應的中斷函數裏面自定義(類似於回撥)但是,這樣顯得有些麻煩,我們可以在啓動程式碼裏面,將所有的中斷動作函數定義爲一個,如下:在这里插入图片描述
我們是如何知道是哪一個中斷產生了呢,我們可以通過NVIC暫存器,遍歷中斷編號,找出對應的中斷信號,具體的程式碼如下:

void IRQ_Desacth()
{
 int i,IRQ_NU;
 //回圈遍歷60箇中斷,找到動作中斷編號
 for(i=0;i<60;i++)
 {
  if(i<I2C1_ER_IRQn)
  {
   if((NVIC->IABR[0]>>i)& 1)
   {
    IRQ_NU=i;
    break;
   }
  }
  else if(i>=32)
  {
   if((NVIC->IABR[1]>>(i-I2C1_ER_IRQn))& 1)
   {
    IRQ_NU=i;
    break;
   }
  } 
 }
 switch(IRQ_NU)
 {
  case EXTI0_IRQn: 
  case EXTI2_IRQn: 
  case EXTI3_IRQn:   
  case EXTI4_IRQn:
   //?ж????
   IRQ_Events[IRQ_NU].irqpHandle();
   //中斷結束之後,需要重新置1
   EXTI->PR |= (1<<(IRQ_NU-6));   //重新置1 
   NVIC->ICPR[0] |= (1<<IRQ_NU);   //中斷解掛
   break;
  case TIM2_IRQn:
   IRQ_Events[IRQ_NU].irqpHandle();
   TIM2->SR &= ~(1<<0);        //中斷結束後,跟新中斷標記
   NVIC->ICPR[0] |= (1<<IRQ_NU);  //中斷解掛
   break;
  case TIM3_IRQn:
   IRQ_Events[IRQ_NU].irqpHandle();
   TIM3->SR &= ~(1<<0);        
   NVIC->ICPR[0] |= (1<<IRQ_NU);  
   break;
  case TIM4_IRQn:
   IRQ_Events[IRQ_NU].irqpHandle();
   TIM4->SR &= ~(1<<0);        
   NVIC->ICPR[0] |= (1<<IRQ_NU);  
   break;
  case TIM5_IRQn:
   IRQ_Events[IRQ_NU].irqpHandle();
   TIM5->SR &= ~(1<<0);        
   NVIC->ICPR[1] |= (1<<(IRQ_NU-32));  
   break;
  case USART1_IRQn:
   // USART1->SR &= ~(1<<9);
   IRQ_Events[IRQ_NU].irqpHandle();
   NVIC->ICPR[1] |= (1<<(IRQ_NU-32));  
   break;
  case RTC_IRQn:
   RTC->CRL &= ~(1<<0);    
   IRQ_Events[IRQ_NU].irqpHandle();
   NVIC->ICPR[1] |= (1<<IRQ_NU);  
   break;
  case ADC1_IRQn:
   ADC1->SR  &= ~(1<<0);    
   IRQ_Events[IRQ_NU].irqpHandle();
   NVIC->ICPR[1] |= (1<<IRQ_NU);  
   break;
 }
}

說明:並不是每一箇中斷都需要解掛,置1,這個需要看使用者手冊,對應的中斷解釋,這裏,我對中斷時間進行封裝,就是前面所說的函數指針,IRQ_Events[IRQ_NU].irqpHandle();這一步,就是在執行中斷產生之後,需要做的事。封裝了一個結構體。

三、收穫與意義

1、收穫與意義:總的來說,現在的ARM開發,都是呼叫庫函數,很多時候,我們並不知道他的流程是怎麼樣的,那個使用起來也是懵懵的,暫存器實現,就是完整的從底層學起,雖然是很麻煩,還需要去看流程圖,設定哪些暫存器,但是,這個學懂之後,去使用庫函數就會更加明白,那些庫函數,內部其實就是這樣實現的,所以,有必要學習一下這個,這樣也可以自己去封裝。

原始碼下載

1、下載地址:百度網路硬碟:提取碼:w0rs