main函數(shù)分析:
#include "stm32f10x.h"
void GPIO_Config(void)//GPIO配置
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能gpioc的時(shí)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //選擇管腳PC.13作LED燈
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管腳速度為50M
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //設(shè)置輸出模式為推挽輸出
GPIO_Init(GPIOC, &GPIO_InitStructure); //將上述設(shè)置寫(xiě)入到GPIOC里去
}
void NVIC_Config(void) //中斷控制器的配置
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //優(yōu)先組設(shè)置
NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn ; //TIM2中斷選通
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //搶占優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中斷控制
NVIC_Init(&NVIC_InitStructure);
}
void Timer_Config(void) //定時(shí)器的配置
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); //Timer2 時(shí)鐘使能
TIM_DeInit(TIM2); //復(fù)位TIM2定時(shí)器
TIM_TimeBaseStructure.TIM_Period=1000; //定時(shí)器周期
TIM_TimeBaseStructure.TIM_Prescaler=36000-1; //預(yù)分頻數(shù)
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //TIM2時(shí)鐘分頻,為1表示不分頻
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//定時(shí)器計(jì)數(shù)為向上計(jì)數(shù)模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定時(shí)器2的溢出標(biāo)志位
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能定時(shí)器2溢出中斷
TIM_Cmd(TIM2, ENABLE); //定時(shí)器2使能
}
int main(void)
{
SystemInit();//初始化時(shí)鐘,配置為72MHz,我試過(guò)將這句注釋掉,好像不影響結(jié)果。查
了一下,在配置
//main函數(shù)之前的啟動(dòng)代碼有這樣一句 LDR R0, =SystemInit,我疑惑的是難
道啟動(dòng)的時(shí)候就配成72Mhz?
GPIO_Config();
NVIC_Config();
Timer_Config();
while(1)
{
;
}
}
中斷服務(wù)函數(shù)
void TIM2_IRQHandler(void)
{
static int flag_bit=0;//定義一個(gè)標(biāo)志位
if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) //判斷中斷溢出標(biāo)志為是否為1
{
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); //清除溢出中斷標(biāo)志位
flag_bit = !flag_bit;
if(flag_bit == 1)
GPIO_SetBits(GPIOC, GPIO_Pin_13); //熄滅LED
if(flag_bit == 0)
GPIO_ResetBits(GPIOC, GPIO_Pin_13); //點(diǎn)亮LED
}
這個(gè)函數(shù)是寫(xiě)在stm32f10x_it.c 里面的,我對(duì)TIM2_IRQHandler()函數(shù)的理解應(yīng)該是這樣的:
首先由定時(shí)器定時(shí),定時(shí)好了產(chǎn)生中斷溢出標(biāo)志位,發(fā)送中斷
然后進(jìn)入中斷服務(wù)函數(shù)TIM2_IRQHandler(),進(jìn)入函數(shù)之后要做的就是清除中斷溢出
標(biāo)志位。
最后再執(zhí)行函數(shù)里的其他內(nèi)容。
定時(shí)器定時(shí)時(shí)間計(jì)算是這兩句:
TIM_TimeBaseStructure.TIM_Period=1000; //定時(shí)器周期
TIM_TimeBaseStructure.TIM_Prescaler=36000-1; //預(yù)分頻數(shù)
Prescaler可以理解為定時(shí)器的基數(shù)是72M / Prescaler+1 = 2000k,也就是500us ,Period 可以理解為要計(jì)數(shù)多少次,這里是1000次。 所以就是每500us記一次,計(jì)數(shù)1000次,就是500ms。
公式為:
Period / (72M / (Prescaler+1) )=____ 秒
1000 / (72 M/ (35999+1) ) = 0.5 秒
我有的一些疑問(wèn):1、進(jìn)入中斷函數(shù)之后,定時(shí)器要干些什么,是不是就停止計(jì)數(shù)了?
2、計(jì)數(shù)記到1000發(fā)生中斷,計(jì)數(shù)值是不是有自動(dòng)清零
問(wèn)題先放到這兒,邊學(xué)習(xí),邊解決。
USRT1的配置不改變,主要的就是添加打印函數(shù)實(shí)現(xiàn)串口輸出功能。代碼感覺(jué)可能很長(zhǎng),但無(wú)非就是一些判斷,看看字符串最后一位是不是\0 ,不是的話(huà),遇到轉(zhuǎn)義字符,/n /r 怎么做,以及將數(shù)字轉(zhuǎn)換成字符這些。
這些很多時(shí)候我都沒(méi)注意:
1、 這一句while ( *Data != 0) // 判斷是否到達(dá)字符串結(jié)束符
我們平時(shí)不是都用 \0 嗎? 用0開(kāi)始我還沒(méi)反應(yīng)過(guò)來(lái)。 其實(shí)ASCII的十六進(jìn)制的0 就是\0
如果要使用\0,while ( *Data !='\0') ,主要要加單引號(hào)表字符串,上面沒(méi)有加就是十六進(jìn)制,后面的也就能明白了。
2、stdarg.h這個(gè)函數(shù),以前都沒(méi)見(jiàn)過(guò),但學(xué)習(xí)就是要學(xué)習(xí)新知識(shí)。 知識(shí)改變命運(yùn),我一直都相信這句!
3、char *itoa(int value, char *string, int radix) 是指針函數(shù),返回值是一個(gè)地址
4、其他的都是涉及指針的操作,所以C指針一定要學(xué)好,沒(méi)學(xué)好,不要緊,趁這個(gè)機(jī)會(huì)把它弄明白,當(dāng)我看懂了下面這些,也就明白了。
#ifndef __usart_debug_H
#define __usart_debug_H
#include "stm32f10x.h"
#include //stdarg.h是C語(yǔ)言中C標(biāo)準(zhǔn)函數(shù)庫(kù)的頭文件,目的為讓函數(shù)能夠接收可變參數(shù)。
extern void usart_debug_config(void); //提供給外部函數(shù)調(diào)用usart_debug_config()函數(shù)。
//
// 函數(shù)名:itoa
// 描述 :將整形數(shù)據(jù)轉(zhuǎn)換成字符串
// 輸入 :-radix =10 表示10進(jìn)制,其他結(jié)果為0
// -value 要轉(zhuǎn)換的整形數(shù)
// -buf 轉(zhuǎn)換后的字符串
// -radix = 10
* 輸出 :無(wú)
* 返回 :無(wú)
* 調(diào)用 :被USART1_printf()調(diào)用
*
static char *itoa(int value, char *string, int radix)
{
int i, d;
int flag = 0;
char *ptr = string;
//This implementation only works for decimal numbers.
if (radix != 10)
{
*ptr = 0;
return string;
}
if (!value)
{
*ptr++ = 0x30;
*ptr = 0;
return string;
}
//if this is a negative value insert the minus sign.
if (value < 0)
{
*ptr++ = '-';
// Make the value positive.
value *= -1;
}
for (i = 10000; i > 0; i /= 10)
{
d = value / i;
if (d || flag)
{
*ptr++ = (char)(d + 0x30);
value -= (d * i);
flag = 1;
}
}
// Null terminate the string.
*ptr = 0;
return string;
} //NCL_Itoa
// 函數(shù)名:USART1_printf
//描述 :格式化輸出,類(lèi)似于C庫(kù)中的printf,但這里沒(méi)有用到C庫(kù)
//輸入 :-USARTx 串口通道,這里只用到了串口1,即USART1
// -Data 要發(fā)送到串口的內(nèi)容的指針
// -... 其他參數(shù)
// 輸出 :無(wú)
// 返回 :無(wú)
//調(diào)用 :外部調(diào)用
// 典型應(yīng)用USART1_printf( USART1, "\r\n this is a demo \r\n" );
// USART1_printf( USART1, "\r\n %d \r\n", i );
// USART1_printf( USART1, "\r\n %s \r\n", j );
//
static void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...)
{
const char *s;
int d;
char buf[16];
va_list ap; // va_list ap 和 va_start(ap, Data)以及后面的va_arg() 都來(lái)自 stdarg.h
va_start(ap, Data);//具體用法請(qǐng)參照相關(guān)資料。
while ( *Data != 0) // 判斷是否到達(dá)字符串結(jié)束符
{
if ( *Data == 0x5c ) // '\' ASCII表 0x5c就是轉(zhuǎn)義字符'\'
{
switch ( *++Data )
{
case 'r': //回車(chē)符
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx, 0x0d);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
Data ++;
break;
case 'n': //換行符
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx, 0x0a);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
Data ++;
break;
default:
Data ++;
break;
}
}
else if ( *Data == '%')
{ //
switch ( *++Data )
{
case 's': //字符串
s = va_arg(ap, const char *);
for ( ; *s; s++)
{
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
Data++;
break;
case 'd': //十進(jìn)制
d = va_arg(ap, int);
itoa(d, buf, 10);
for (s = buf; *s; s++)
{
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
Data++;
break;
default:
Data++;
break;
}
} //end of else if
else {
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx, *Data++);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
}
#endif // __USART1_H
軟件仿真中的效果圖:
波形圖 IO口狀態(tài) 串口輸出 都在下面圖里了