#include"stm32f10x_conf.h"
void delay_ms(u32 ms); 声明延时函数
void GPIO_Config(void); 声明GPIO配置函数
int main(void) 主程序
{
SystemInit(); 初始化系统时钟默认72MHZ
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); 使能GPIOA时钟
GPIO_Config(); 调用GPIO配置函数
while(1)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0); GPIOA的 Pin0脚置1(高电平)
delay_ms(1000); 延时1000ms=1s
GPIO_ResetBits(GPIOA,GPIO_Pin_0);GPIOA的 Pin0脚置0(低电平)
delay_ms(1000); 延时1000ms=1s
}
}
void GPIO_Config(void) 配置GPIOA函数
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
void delay_ms(u32 ms) 延时函数,重点!
{
int temp;
SysTick->CTRL=0x01;
SysTick->LOAD=9000*ms;
SysTick->VAL=0x00;
do
{
temp=SysTick->CTRL;
}
while((temp&0x01)&&(!(temp&(1<<16))));
}
下面我把延时函数系统讲一下:
延时函数主要用到的是systick,也叫滴答系统时钟。我只知道systick其中的一个功能就是可以做定时器用而且非常精准,其他的许多功能还在研究。
systick的4个寄存器,CTRL-控制寄存器,LOAD-重装载寄存器,VAL-当前值寄存器,CALIB-校准寄存器。
SYSTICK_CTRL 寄存器中只有0,1,2,16这四位是有效的。
第 0 位:ENABLE,Systick 使能位 (0:关闭 Systick 功能;1:开启 Systick功能)
第 1 位:TICKINT,Systick 中断使能位 (0:关闭 Systick 中断;1:开启Systick 中断)
第 2 位:CLKSOURCE,Systick 时钟源选择 (0:使用 HCLK/8 作为 Systick时钟;1:使用 HCLK 作为 Systick 时钟)
第16 位:COUNTFLAG,有些人这样说的: SysTick 已经数到了 0,则该位为 1。如果读取该位,该位将自动清零(我不太理解这句话,我的疑问是SYSTICK数到了0是不是说VAL寄存器从重装载值递减到了0,第二个就是SYSTICK数到0之后这个位是保持1还是返回到0,读取该位自动清0是什么时候读取。)这个先埋个疑问一会说说我的理解。
SYSTICK_LOAD重装载寄存器,不用多说,假如你让systick_val寄存器从100递减到0,这个寄存器里面装的就是100,只不过用二进制表示,但是这个寄存器虽然是32位的,但只有24位有效,其最高的八位保留,它能装入最大值为0xFFFFFF(0xFFFFFF==0x00FFFFFF,C语言中二者的值是一样的,如0x01==0x0001==0x00000001)。
SYSTICK_CAL 当前值寄存器,书上说读取它时返回当前寄存器的值,对他进行写操作则清0,同时也对上面所说SYSTICK_CTRL中 第16位清0。我理解是这样的,这个寄存器里面的值是不断变化的,一个指令周期完成一次自减,至于如何自减我们无须讨论太多,只需知道若选择1KHZ的频 率,则它在1秒的时间里可以变化1000次,每变化一次便可自减一次,换句话说,我们将1000装入它时开始计时,逐步递减999,998......到 它减到0计时结束刚好耗时1秒。
SYSTICK_CALIB 校准寄存器,这个还没研究透(都说一般用不到,但最终还得弄明白它),sorry!
那么SYSTICK 的工作流程到底是怎样实现定时的呢,下面以定时1ms为例。
1s=1000ms=1000000us,首先配置SysTick->CTRL=0x01;对照上面便知,先使能SYSTICK,关闭了中断,选择HCLK/8位工作频率,状态标志位清零;然后SysTick->LOAD=9000*ms;设置重装载寄存器的值,我们的系统时钟是72MHZ,上面我们选择的8分频也就是9MHZ,也就是说1ms变化9000次,然后SysTick->VAL=0x00;清 零当前寄存器值,前面我们说了,VAL寄存器是不断自减的,并且只要它为0(无论是被写入0还是自减到0)就自动装入LOAD寄存器中的值再一次开始递 减,这样循环往复,那么VAL为0时的第二个动作是将CTAL寄存器的16位状态位置1,而我们就是通过读取CTAL寄存器的16位状态位才能知道是否到 了1ms,这里也说一下上面埋下的疑问,这个状态位其实像一个监视器一样监视着VAL寄存器,只要VAL为0,它立刻置1,并且一直保持,直到对它读取时 它才又一次被清0。
紧接着判断1ms是否达到,do{...}while(...)语句,主要判断CTRL寄存器中的16位是否为1,temp=SysTick->CTRL;将CTRL中的值传递给变量temp,注意while中的是重点,while((temp&0x01)&&(!(temp&(1<<16))));说 实话我看到这个语句真的不知道什么意思,C语言学的还不算透彻,这里面其实不只是判断CTRL中的标志位,还判断了systick是否失能,如果 systick失能则在此处为0继续执行程序不再进行判断。先整体看看这个语句的结构,语句1&&语句2,这个“&&” 表示逻辑与操作和按位与“&”相似,是有顺序执行的,先判断语句1,如果是真则判断语句2,语句2为真则结果为真(1),语句2为假则结果为假 (0);若语句1为假,则不再对语句2进行判断,直接输出为假(0)。那么如上,语句1(temp&0x01)是判断systick是使能还是失 能,若使能则语句1为真继续判断语句2,若失能则语句1为假,则整个语句为假程序向下执行,不再循环。既然语句1为真则继续判断语句2(!(temp&(1<<16)),它才是真正判断CTRL标志位的,这个应该很好理解,如果CTRL标志位为1,则语句2为假(0),则整个语句为假(0),向下执行程序(systick正好产生了1ms的时间间隔)。
这么点程序,其实要说的可真不少,ARM真的太深奥了。以上这种方式是对寄存器直接操作,也可用stm32 提供的SYSTICK函数,不过我感觉想弄懂它是怎么工作的还是要学习它相关的寄存器。函数库只是为了方便开发,在开发大程序是有一定的优势。好了最后看一下我的软仿图吧: