在我们日常使用单片机的时候,延时一般采用循环的方式,但是这样的方式只能用于粗略的延时,但我们需要精准的时间控制的时候,便需要利用定时器获得精确的延时。
本次采用TM4内的滴答定时器。
滴答定时器结构比较简单,在TM4内部是一个24位自减的计数器。
SysTickPeriodSet(SysCtlClockGet()/1000); // 1ms
SysTickIntRegister(SysTick_IntHandler);
SysTickIntEnable();
SysTickEnable();
1、首先进行周期设置,用系统频率除以1000,表示1ms的中断。
计算方法:定时器的工作原理便是在系统时钟驱动下进行计数,假设系统时钟频率是10KHz,代表1秒钟可以计10000个数,即:即计一个数的时间t = 1/10000 s = 0.1 ms
,那么如果我要设置一个一毫秒的中断,即需要数10个数,因为10*(1/10000) = 1/1000 s = 1ms
。
因此对于SysTickPeriodSet()
这个函数来说,我需要一毫秒的中断,便将系统时钟除以1000作为参数传进去即可。
2、然后设置中断函数的地址
即SysTick_IntHandler
便是中断处理函数的函数名称,代表该函数的地址。
3、使能中断,打开定时器
SysTickIntEnable();
SysTickEnable();
4、写中断处理函数
这里是设置了一个全局变量,每进入一次中断便将其加1。
void SysTick_IntHandler(void)
{
System_Time_ms++;
}
5、测试:见文末测试例程1。
首先我们需要知道系统的当前精确时间,这里实现了一个micros()
函数,以微秒为单位。
uint32_t micros(void)
{
register uint32_t ms, tick;
do{
ms = System_Time_ms;
tick = HWREG(NVIC_ST_CURRENT);
}while(ms != System_Time_ms);
return (ms+1)*1000 - tick/usTicks;
}
在这个函数中,因为滴答定时器的中断是1ms,所以System_Time_ms
便是系统运行的ms数,HWREG(NVIC_ST_CURRENT)
是针对寄存器的操作,获得当前滴答定时器内部的计数值。usTicks = Clock/1000000
,所以tick/usTicks = tick*(1000000/Clock)
,即将定时器计数值转换为实际的us数,然后通过(ms+1)*1000 - tick/usTicks
的运算,即得到了us级的系统时间。
这里的逻辑就是进入函数时记录当前时间,然后一直用实时时间和开始时间进行比较,作差,得到延时时间。
void delay_us(uint32_t nus)
{
uint32_t t0 = micros();
while(micros() - t0 < nus)
;
}
void delay_ms(uint32_t nms)
{
uint32_t t0 = micros();
while(micros() - t0 < nms*1000)
;
}
测试:见测试例程2。
功能:打印系统当前时间,以毫秒为单位。
#include
#include
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/systick.h"
#include
uint32_t System_Time_ms = 0;
void delay_ms(int n)
{
for(int i = 0; i < n; i++)
SysCtlDelay(SysCtlClockGet()/3000);
}
//重写fputc函数以支持printf
int fputc(int ch, FILE *f)
{
UARTCharPut(UART0_BASE,(unsigned char)ch);//如果用其他串口,只需修改基址(UART0_BASE)即可。
return ch;
}
void SysTick_IntHandler(void)
{
System_Time_ms++;
}
int main()
{
SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
//初始化串口0
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0))
;
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA))
;
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
UARTConfigSetExpClk(UART0_BASE, 16000000, 115200, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
//初始化滴答定时器
SysTickPeriodSet(SysCtlClockGet()/1000); // 1ms
SysTickIntRegister(SysTick_IntHandler);
SysTickIntEnable();
SysTickEnable();
while(1)
{
printf("%d\n", System_Time_ms);//打印系统时间
delay_ms(100);
}
}
功能:延时10ms,然后打印出来花费的具体时间。
#include
#include
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/systick.h"
#include "inc/hw_types.h"
#include "inc/hw_nvic.h"
#include
volatile uint32_t usTicks = 0;
volatile uint32_t System_Time_ms = 0;
void delay_us(uint32_t nus);
void delay_ms(uint32_t nms);
int fputc(int ch, FILE *f);
void SysTick_IntHandler(void);
uint32_t micros(void);
void UART0_Init(uint32_t Baud);
void SysTick_Init(void);
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
UART0_Init(115200);
SysTick_Init();
uint32_t t0,t1;
while(1)
{
t0 = micros();
delay_ms(10);
t1 = micros();
printf("%d\n", t1-t0);//打印系统时间
}
}
void delay_us(uint32_t nus)
{
uint32_t t0 = micros();
while(micros() - t0 < nus)
;
}
void delay_ms(uint32_t nms)
{
uint32_t t0 = micros();
while(micros() - t0 < nms*1000)
;
}
int fputc(int ch, FILE *f)
{
UARTCharPut(UART0_BASE,(unsigned char)ch);
return ch;
}
void SysTick_IntHandler(void)
{
System_Time_ms++;
}
uint32_t micros(void)
{
register uint32_t ms, tick;
do{
ms = System_Time_ms;
tick = HWREG(NVIC_ST_CURRENT);
}while(ms != System_Time_ms);
return (ms+1)*1000 - tick/usTicks;
}
void UART0_Init(uint32_t Baud)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0))
;
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA))
;
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
UARTConfigSetExpClk(UART0_BASE, 16000000, Baud, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
}
void SysTick_Init(void)
{
SysTickPeriodSet(SysCtlClockGet()/1000); // 1ms
SysTickIntRegister(SysTick_IntHandler);
SysTickIntEnable();
SysTickEnable();
usTicks = SysCtlClockGet() / 1000000; // usTicks = 80
}