STM32学习之通用定时器的使用

发布者:SerendipityLove最新更新时间:2021-05-31 来源: eefocus关键字:STM32  通用定时器  RTC 手机看文章 扫描二维码
随时随地手机看文章

简介:STM32一共有8个通用16位Timer,其中TIMER1和TIMER8是高级定时器,其它的TIMER2~TIMER7是普通定时器。此外还有一个Systick(系统滴答定时器),这个定时器通常在操作系统中作为系统的任务切换周期。还有一个RTC,是一个毫秒定时器,支持秒级中断,用来做实时时钟计数器。看门狗定时器 也可以算一个。


8个定时器中,Timer1 和Timer8是由APB2(输出最高频率为72MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为72MHz。Timer2~Timer7则是由APB1(输出最高频率为36MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为36MHz。


1、如何进行程序编写


这里我通过定时器来控制一个LED亮0.5s 灭0.5s ,交替闪烁。当然要让定时器正常工作起来,还要配置中断NVIC。定时器计数到某个数,产生中断,从而进入中断服务程序,点亮LED灯。


main函数分析:


#include "stm32f10x.h"


void GPIO_Config(void)//GPIO配置


{


GPIO_InitTypeDef GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能gpioc的时


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; //设置输出模式为推挽输出


GPIO_Init(GPIOC, &GPIO_InitStructure); //将上述设置写入到GPIOC里去


}




void NVIC_Config(void) //中断控制器的配置


{


NVIC_InitTypeDef NVIC_InitStructure;


NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //优先组设置


NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn ; //TIM2中断选通


NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级


NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级


NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断控制


NVIC_Init(&NVIC_InitStructure);


}


void Timer_Config(void) //定时器的配置


{


TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); //Timer2 时钟使能


TIM_DeInit(TIM2); //复位TIM2定时器


TIM_TimeBaseStructure.TIM_Period=1000; //定时器周期


TIM_TimeBaseStructure.TIM_Prescaler=36000-1; //预分频数


TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //TIM2时钟分频,为1表示不分频


TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//定时器计数为向上计数模式


TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);


TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器2的溢出标志位


TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能定时器2溢出中断


TIM_Cmd(TIM2, ENABLE); //定时器2使能


}






int main(void)


{


SystemInit();//初始化时钟,配置为72MHz,我试过将这句注释掉,好像不影响结果。查


了一下,在配置


//main函数之前的启动代码有这样一句 LDR R0, =SystemInit,我疑惑的是难


道启动的时候就配成72Mhz?


GPIO_Config();


NVIC_Config();


Timer_Config();


while(1)


{


;


}


}








中断服务函数


void TIM2_IRQHandler(void)


{


static int flag_bit=0;//定义一个标志位




if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) //判断中断溢出标志为是否为1


{


TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); //清除溢出中断标志位


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); //点亮LED


}




这个函数是写在stm32f10x_it.c 里面的,我对TIM2_IRQHandler()函数的理解应该是这样的:


首先由定时器定时,定时好了产生中断溢出标志位,发送中断


然后进入中断服务函数TIM2_IRQHandler(),进入函数之后要做的就是清除中断溢出


标志位。


最后再执行函数里的其他内容。


定时器定时时间计算是这两句:


TIM_TimeBaseStructure.TIM_Period=1000; //定时器周期


TIM_TimeBaseStructure.TIM_Prescaler=36000-1; //预分频数


Prescaler可以理解为定时器的基数是72M / Prescaler+1 = 2000k,也就是500us ,Period 可以理解为要计数多少次,这里是1000次。 所以就是每500us记一次,计数1000次,就是500ms。


公式为:


Period / (72M / (Prescaler+1) )=____ 秒


1000 / (72 M/ (35999+1) ) = 0.5 秒






我有的一些疑问:1、进入中断函数之后,定时器要干些什么,是不是就停止计数了?


2、计数记到1000发生中断,计数值是不是有自动清零


问题先放到这儿,边学习,边解决。

2、仿真结果观察



前面第三章已经过如何仿真波形的步骤,可以参看前面。点击setup 按钮 会弹出一个窗口,在窗口的右上边,有个new的按钮,点击后输入 PORTC.13





仿真运行结果如下:



可以从仿真结果中观察到,方波的周期为一秒。占空比为0.5 ,跟预期一致。



3、对第四章串口的补充

第四章介绍了串口的打印函数printf 是如何调用实现的。但要使用keil自带的微库microLIB ,那能不能不使用这个微库呢。我参照野火的教程,修改了程序,自己编写usart_printf()函数来实现打印的功能。




USRT1的配置不改变,主要的就是添加打印函数实现串口输出功能。代码感觉可能很长,但无非就是一些判断,看看字符串最后一位是不是 ,不是的话,遇到转义字符,/n /r 怎么做,以及将数字转换成字符这些。


这些很多时候我都没注意:


1、 这一句while ( *Data != 0) // 判断是否到达字符串结束符


我们平时不是都用 吗? 用0开始我还没反应过来。 其实ASCII的十六进制的0 就是


如果要使用,while ( *Data !='') ,主要要加单引号表字符串,上面没有加就是十六进制,后面的也就能明白了。


2、stdarg.h这个函数,以前都没见过,但学习就是要学习新知识。 知识改变命运,我一直都相信这句!


3、char *itoa(int value, char *string, int radix) 是指针函数,返回值是一个地址


4、其他的都是涉及指针的操作,所以C指针一定要学好,没学好,不要紧,趁这个机会把它弄明白,当我看懂了下面这些,也就明白了。




#ifndef __usart_debug_H


#define __usart_debug_H




#include "stm32f10x.h"


#include //stdarg.h是C语言中C标准函数库的头文件,目的为让函数能够接收可变参数。




extern void usart_debug_config(void); //提供给外部函数调用usart_debug_config()函数。


//


// 函数名:itoa


// 描述 :将整形数据转换成字符串


//输入 :-radix =10 表示10进制,其他结果为0


// -value 要转换的整形数


// -buf 转换后的字符串


// -radix = 10


* 输出 :无


* 返回 :无


* 调用 :被USART1_printf()调用


*


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






// 函数名:USART1_printf


//描述 :格式化输出,类似于C库中的printf,但这里没有用到C库


//输入 :-USARTx 串口通道,这里只用到了串口1,即USART1


// -Data 要发送到串口的内容的指针


// -... 其他参数


//输出 :无


// 返回 :无


//调用 :外部调用


// 典型应用USART1_printf( USART1, "rn this is a demo rn" );


//USART1_printf( USART1, "rn %d rn", i );


//USART1_printf( USART1, "rn %s rn", 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() 都来自 stdarg.h


va_start(ap, Data);//具体用法请参照相关资料。




while ( *Data != 0) // 判断是否到达字符串结束符


{


if ( *Data == 0x5c ) // '' ASCII表 0x5c就是转义字符''


{


switch ( *++Data )


{


case 'r': //回车符


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': //十进制


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口状态 串口输出 都在下面图里了


关键字:STM32  通用定时器  RTC 引用地址:STM32学习之通用定时器的使用

上一篇:基于STM32的电池管理系统触摸屏设计方案
下一篇:STM32ADC模块速度配置问题

推荐阅读最新更新时间:2024-11-13 12:59

STM32系列的处理器介绍
应用背景 如果你正为项目的处理器而进行艰难的选择:一方面抱怨16位单片机有限的指令和性能,另一方面又抱怨32位处理器的高成本和高功耗,那么,基于ARM Cortex-M3内核的STM32系列处理器也许能帮你解决这个问题。使你不必在性能、成本、功耗等因素之间做出取舍和折衷。 即使你还没有看完STM32的产品手册,但对于这样一款融合ARM和ST技术的 新生儿 相信你和我一样不会担心这款针对16位MCU应用领域的32位处理器的性能,但是从工程的角度来讲,除了芯片本身的性能和成本之外,你或许还会考虑到开发工具的成本和广泛度;存储器的种类、规模、性能和容量;以及各种软件获得的难易,我相信你看完本专题会得到一个满意的答案。 对于在16位MCU领
[单片机]
STM32入门学习笔记之文件系统FatFs的移植3
21.2.2 ffsystem.c文件的修改 (1)内存分配ff_memalloc void* ff_memalloc( UINT msize ) { return ( void* )mymalloc( SRAMIN, msize ) ; } (2)内存释放ff_memfree void ff_memfree( void* mblock ) { myfree( SRAMIN, mblock ) ; } 21.2.3 exfuns.c与exfuns.h文件的创建 (1)创建exfuns.h文件,并输入以下代码。 #ifndef _EXFUNS_H #define _EXFUNS_H #i
[单片机]
<font color='red'>STM32</font>入门学习笔记之文件系统FatFs的移植3
STM32外部中断程序
原理图: 程序分析: #include led.h #include delay.h #include key.h #include sys.h #include usart.h #include exti.h #include beep.h //STM32开发板实验5 //外部中断实验 int main(void) { delay_init(); //延时函数初始化 NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init(9600);//串口初始化为9600 LED_Init();
[单片机]
<font color='red'>STM32</font>外部中断程序
stm32 Bootloader设计(YModem协议)
相信很多人都希望,不开盖就可以对固件进行升级吧,就像手机那些。下文中的bootload就来实现这样的功能。 前段时间有项目关于Bootload设计。所以就仔细的去了研究了一翻。以前都是用的stm32官方的,没有去深入了解。这次做完了过后,发现官方的版本存在一些问题。比如说YModem传送过程中,完全没有对数据区进行效验,只是核对了下编号,就进行烧写。整个程序完全为阻塞式,浪费了大量的cpu做无用功。当然这在升级程序方面也用不了多少时间。有一个重要的问题,官方代码只可以用超级终端进行传输。这样如果你用的是64位的win7,那就没有办法升级。因为只有xp或32位的win7才可以使用 超级终端。64位的win7下超级终端没办法使
[单片机]
<font color='red'>stm32</font> Bootloader设计(YModem协议)
STM32 RTC日历程序
STM32的RTC只是一个32bit 计数器,没有年月日星期等信息,比起专用RTC芯片那差很远。要实现时间日期功能,要程序实现。记得linux下面的时间也是一个32bit的计数器。一查,原来网上牛人多的是,早已实现。而且用此法实现,非常简单。完全不用考虑什么闰年,大小月等。关键函数mktime,标准函数库函数。 以下程序作者为 jjldc (九九) rtc_time.h #ifndef _RTC_TIME_H_ #define _RTC_TIME_H_ #include time.h extern struct tm Time_ConvUnixToCalendar(time_t t); extern time
[单片机]
串口通信(用CubeMX学习STM32)
前言: STM32串口介绍 串行通信是单片机与外部设备或其他计算机交换信息的一个方式, 数据一位一位的按顺序传送, 其优点是只需要一条传输线, 协议简单, 但是缺点就是传送速度较慢。 串口是单片机上非常便捷的一个工具, 当写程序需要调试的时候, 它可以很方便的提供调试方法, 只要在一些关键代码执行的地方, 通过串口给串口调试助手发送相关信息, 就可以使我们很方便的查看代码在这个位置的执行情况。 下面看一下我所使用的单片机上串口的原理图接线 外部的发送端TXD就是单片机串口的接收端USART_RX,   外部接收端RXD就是单片机串口的发送端USART_TX TXD : Transmit(TX) Data(D)  Rec
[单片机]
利用stm32自带的正交编码器检测增量式编码器流程总结
由于手术的工频升级机需要自动平层功能,于是着手开始做这方面的工作。硬件选择的是增量式编码器,100脉冲每转,后来了解到stm32的每个定时器的通道1和通道2内置了正交编码器模块,可以直接使用。之前的公司工程师都是用定时器捕捉脉冲,然后自行处理的,我看了下代码挺麻烦的,现在用了stm32自带的感觉就容易多了。找了官方的软件说明,看了下网上已有的例子,一个下午就基本在我的系统架构中添加了这个设备,然后对这个设备初始化,设置上层接口API。最后看些例子将16位计数器软件扩展到32位。就顺利的完成了基本模块的第一步工作了。以后则需要将采集的到数据与楼层做一个好的数据结构结合在一起,方便调用和维护了。 下面贴上我的基本思路和相关软件代
[单片机]
利用<font color='red'>stm32</font>自带的正交编码器检测增量式编码器流程总结
STM32让printf通过串口打印及自定义printf函数
在嵌入式系统中,通过串口打印log是非常重要的调试手段,但是直接调用底层驱动打印信息非常不方便,在c语言中一般使用printf打印基本的显示信息,而默认printf的结果不会通过串口发送,所以需要对printf的输出进行重定向。 有时候需要同时从多个串口输出信息,如果仍然想通过printf函数输出信息,就需要自己写printf的实现。 一. 初始化端口和配置 对串口用到的GPIO进行配置,并对串口的参数进行初始化。 二. 宏定义并实现具体的发送函数 代码在编译时首先判断__GNUC__有无定义,之后将PUTCHAR_PROTOTYPE替换成具体的定义。在keil5中,使用fputc函数,所以其实
[单片机]
<font color='red'>STM32</font>让printf通过串口打印及自定义printf函数
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved