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