Cubieboard2裸机开发之(四)定时器操作

时间:2021-04-06 19:32:39

前言

在Cubieboard2裸机开发之(三)里用到了一个延时函数delay,它的延时时间是不精确的,因此为了能够精确延时,就需要定时器的配合。定时器可以精确延时的一个重要原因是它的计时时钟(或者说频率)是精确的,计时时钟越小,能实现的延时时间就越小。

A20的定时器模块比较强大,它不仅有6个普通的定时器,还有4个高速定时器,计时频率可达上百MHz,更重要的是它们操作起来非常简单、易懂。

一、目的

学习使用A20的普通定时器,实现精确延时。

二、源代码说明

start.S文件。首先禁止CPU的IRQ和FIQ,设置为管理模式,然后设置堆栈指针,最后调用C语言的main函数。

 /*
* (C) Copyright conan liang <lknlfy@163.com>
*
*/ /* global entry point */
.globl _start
_start: b reset reset:
/* disable IRQ & FIQ, set the cpu to SVC32 mode */
mrs r0, cpsr
and r1, r0, #0x1f
teq r1, #0x1a
bicne r0, r0, #0x1f
orrne r0, r0, #0x13
orr r0, r0, #0xc0
msr cpsr, r0
/* setup stack, so we can call C code */
ldr sp, =( * )
/* jump to main function */
bl main
loop:
b loop

main.c文件。首先初始化LED所在IO管脚,设置为输出功能,并且输出低电平,即一开始两个LED是熄灭的,接着初始化定时器0,包括设置它的时钟,工作模式等。

 #include "timer.h"
#include "io.h" /* reg define for IO of LEDs */
#define SUNXI_PIO_BASE (0x01C20800)
#define PH_CFG2 (SUNXI_PIO_BASE + 0x104)
#define PH_DAT (SUNXI_PIO_BASE + 0x10C) /* set two LEDs on */
static void set_led_on(void)
{
unsigned int tmp; /* PH20 and PH21 output 1 */
tmp = readl(PH_DAT);
tmp |= (0x1 << );
tmp |= (0x1 << );
writel(tmp, PH_DAT);
} /* set two LEDs off */
static void set_led_off(void)
{
unsigned int tmp; /* PH20 and PH21 output 0 */
tmp = readl(PH_DAT);
tmp &= ~(0x1 << );
tmp &= ~(0x1 << );
writel(tmp, PH_DAT);
} static void led_init(void)
{
unsigned int tmp; /* configure PH20 and PH21 output */
tmp = readl(PH_CFG2);
tmp &= ~(0x7 << );
tmp &= ~(0x7 << );
tmp |= (0x1 << );
tmp |= (0x1 << );
writel(tmp, PH_CFG2);
/* set PH20 and PH21 output 0 */
tmp = readl(PH_DAT);
tmp &= ~(0x1 << );
tmp &= ~(0x1 << );
writel(tmp, PH_DAT);
} /* C code entry point */
int main(void)
{
/* init led */
led_init();
/* init timer0 */
sunxi_timer0_init(); while () {
set_led_on();
udelay();
set_led_off();
udelay();
} return ;
}

timer.c文件。一个初始化函数,一个微妙延时函数,这里设置定时器的计时频率为6MHz。

 #include "timer.h"
#include "io.h" /* 6MHz for timer0 count freq*/
#define TIMER0_HZ (6000000) #if 0
static void sunxi_timer0_start(void)
{
unsigned int tmp; tmp = readl(TMR0_CTRL_REG);
tmp |= ( << TMR0_EN);
writel(tmp, TMR0_CTRL_REG);
} static void sunxi_timer0_stop(void)
{
unsigned int tmp; tmp = readl(TMR0_CTRL_REG);
tmp &= ~( << TMR0_EN);
writel(tmp, TMR0_CTRL_REG);
}
#endif /* accurate delay */
void udelay(unsigned int usec)
{
unsigned int count;
unsigned int tmp; /* write interval value */
count = (TIMER0_HZ / ) * ((unsigned int)usec);
writel(count, TMR0_INTV_VALUE_REG); /* reload and start timer0 must be operated at the same time */
tmp = readl(TMR0_CTRL_REG);
tmp |= ( << TMR0_RELOAD);
tmp |= ( << TMR0_EN);
writel(tmp, TMR0_CTRL_REG);
/* wait for interrupt */
while (!(readl(TMR_IRQ_STA_REG) & ( << TMR0_IRQ_PEND)));
/* clear timer0 interrupt */
tmp = readl(TMR_IRQ_STA_REG);
tmp |= ( << TMR0_IRQ_PEND);
writel(tmp, TMR_IRQ_STA_REG);
} void sunxi_timer0_init(void)
{
unsigned int tmp; /* single mode, /4 divide, clock source is OSC24M, reload . so clk_freq = 24M / 4 = 6M*/
tmp = (0x1 << TMR0_MODE) | (0x2 << TMR0_CLK_PRES) | (0x1 << TMR0_CLK_SRC) | (0x1 << TMR0_RELOAD);
writel(tmp, TMR0_CTRL_REG);
/* clear timer0 interrupt */
tmp = readl(TMR_IRQ_STA_REG);
tmp |= ( << TMR0_IRQ_PEND);
writel(tmp, TMR_IRQ_STA_REG);
/* enable timer0 interrupt */
tmp = readl(TMR_IRQ_EN_REG);
tmp |= ( << TMR0_IRQ_EN);
writel(tmp, TMR_IRQ_EN_REG);
}

三、验证

使用arm-linux-gnueabihf工具编译后生成timer.b文件,再使用mksunxiboot工具在timer.b文件前面加上一个头部,最终生成timer.bin文件,使用以下命令将timer.bin文件烧写到TF中:

#sudo dd if=./timer.bin of=/dev/sdb bs=1024 seek=8

将TF卡插入Cubieboard2,上电即可看到两个LED同时闪烁,并且闪烁周期为2秒(亮1秒,灭1秒),效果不好用图片展示,因此就不上图了。