上篇I/O控制文章中,程序需要延时时,我们是利用循环语句来实现。这种方法的延时简单,但不是很精确。当需要精确延时时,就不能采用这种方法了。一般是利用定时器来实现。在这里,我们就介绍一下s3c2440定时器。
► S3C2440的时钟源
在讲解之前,先介绍一下s3c2440时钟系统。一般来说,MCU的主时钟源主要是外部晶振或外部时钟,而用的最多的是外部晶振。在正确情况下,系统内所使用的时钟都是外部时钟源经过一定的处理得到的。由于外部时钟源的频率一般不能满足系统所需要的高频条件,所以往往需要PLL(锁相环)进行倍频处理。在s3c2440中,有2个不同的PLL,一个是MPLL,另一个是UPLL。
UPLL是给USB提供48MHz。在这里,我们主要介绍MPLL。外部时钟源经过MPLL处理后能够得到三个不同的系统时钟:FCLK、HCLK和PCLK。
♥ FCLK是主频时钟,用于ARM920T内核;
♥ HCLK用于AHB总线设备,如ARM920T,内存控制,中断控制,LCD控制,DMA以及USB主模块;
♥ PCLK用于APB总线设备,如外围设备的看门狗,IIS,I2C,PWM,MMC接口,ADC,UART,GPIO,RTC以及SPI。
这三个系统时钟(FCLK、HCLK和PCLK)是有一定的比例关系,这种关系是通过寄存器CLKDIVN中的HDIVN位和PDIVN位来控制的,因此我们只要知道了FCLK,再通过这两位的控制,就能确定HCLK和PCLK。
而FCLK是如何得到的呢?它是通过输入时钟(即外部时钟源)的频率,经过一个计算公式(具体公式请查阅数据手册)得到的,这个计算公式还需要三个参数(MDIV、PDIV、SDIV),而这三个参数是经过寄存器MPLLCON配置得到的。
最后,我们用最清晰的线路来绘制一下时钟的产生过程:
外部时钟源→通过寄存器MPLLCON得到FCLK→再通过寄存器CLKDIVN得到HCLK和PCLK。
这个配置过程在启动文件中就已完成。例如,外部晶振为12MHz,进过MPLL倍频以后得到400MHz的FCLK,而FCLK、HCLK、PCLK之间的比例关系为1:4:8,因此HCLK为100MHz,PCLK为50MHz。
► S3C2440的定时器特性
S3C2440有5个16位的定时器,定时器0~3有脉宽调制功能(PWM)。定时器4没有输出引脚的内部定时器。定时器0有一个用于大电流设备的死区生成器。S3C2440定时器0和1共享一个8位的预分频器,定时器2、3、4共享另一个8位预分频器。每个定时器有一个时钟分频器,其中以生成5种不同的分频信号。
每个定时器有一个自己的由定时器时钟驱动16位递减计数器,当递减计数器为零时,定时器中断请求生成通知CPU定时器操作完成。
S3C2440定时器特性如下:
◎ 5个16位定时器
◎ 两个8位预分频器和2个4位分频器
◎ 输出波形的可编程任务控制
◎ 自动重载模式或单脉冲模式
◎ 死区生成器
► S3C2440的定时器寄存器
S3C2440定时器:
● 定时器配置寄存器(TCFGn);
● 定时器控制寄存器(TCON);
● 定时器n计数缓存寄存器(TCNTBn),定时器n比较缓存寄存器(TCMPBn);
● 定时器计数观察计数器。
定时器寄存器具体说明请查阅S3C2440芯片手册。
♥ 定时器配置寄存器(TCFGn)
定时器配置寄存器用于配置两个8位预分频器值,该寄存器复位值为0,定时器输出时钟频率计算如下:
定时器输出时钟频率 = PCLK / (预分频器值+1) / 分频值
● TCFG0配置寄存器:[0-7]配置定时器0,1的预分频值;[8-15]配置定时器2,3,4的预分频值。
● TCFG1配置寄存器:[0-3]配置定时器0分频值;[4-7]配置定时器1分频值;[8-11]配置定时器2分频值;[12-15]配置定时器3分频值;[16-149]配置定时器4分频值;
♥定时器控制寄存器(TCON)
TCON寄存器用于控制定时器0~4,寄存器复位值为0。
TCON寄存器 |
描述 |
0~3 |
控制定时器0:使能[0]、手动更新[1]、匹配输出[2]、自动重载[3] |
4 |
死区使能 |
8~11 |
控制定时器1:使能[8]、手动更新[9]、匹配输出[10]、自动重载[11] |
12~15 |
控制定时器2:使能[12]、手动更新[13]、匹配输出[14]、自动重载[15] |
1516~19 |
控制定时器3:使能[16]、手动更新[17]、匹配输出[18]、自动重载19] |
20~22 |
控制定时器4:使能[20]、手动更新[21]、自动重载[23] |
♥ 定时器n计数缓存寄存器(TCNTBn),定时器n比较缓存寄存器(TCMPBn)
定时器计数与比较缓存寄存器用于缓存用户所给的定时值,当配置TCON寄存器定时器n使能“手动更新”时,该值将在下一个定时启动时装载到TCMPn与TCNTn中,重新进行定时。例如,定时器0内部结构如图所示:
TCMP0与TCNT0不能直接对其操作。[page]
♥ 定时器计数观察计数器(TCNTOn)
定时器计数观察计数器保存定时器计数器值(只读)。
► S3C2440定时器操作
定时器启动后,TCNTn开始减一计数,当TCNTn 等于TCMPn时,TOUTn 反转,TCNTn继续减数。当TCNTn= 0 时,TOUTn 再次反转,并触发中断(中断已经使能)。若TCON 设为自动加载,TNCTn/TCMPNn 的值被重装。
图中步骤如下:
1、使能自动重载功能。设置TCNTBn为160,TCMPBn为110。设置手动更新位且配置反相器位(开/关)。手动更行位将TCNTBn和TCMPBn的值分别给TCNTn和TCMPn寄存器,然后设置TCNTBn=80和TCMPBn=40,决定下一个重载值;
2、设置开始位,手动更新位为0,手动更新关闭且自动重载开启,定时器开始倒数计数;
3、当TCNTn的值和TCMPn的值相同,TOUTn的逻辑电平从低变高;
4、当TCNTn为0,中断请求生成且TCNTBn的值装载到一个临时寄存器中。在下一个定时器周期,TCNTn会用该临时寄存器的值重载;
5、在中断服务程序中,TCNTBn和TCMPBn为下一个周期分别设置为80和60;
6、当TCNTn的值和TCMPn的值相同,TOUTn的逻辑电平从低变高;
7、当TCNTn为0,TCNTn用TCNTBn的值自动重载,并触发中断请求;
8、在中断服务程序中,自动重载和自动请求被设无效以停止定时器;
10、就算TCNTn为0,因为自动重载被设为无效,TCNTn不再重载且定时器停止;
11、没有其他的自动请求生成。
► S3C2440定时器初始化
以定时器0为例,启动定时器操作步骤大致如下:
1、设置TCMPB0和TCNTB0寄存器;
2、设置TCON寄存器,计数值进行装载;
3、启动定时器;
4、计数器重新被装载。
- void Timer0_init()
- {
- TCFG0 = 119; //配置定时器0,1的预分频值
- TCFG1 = 0x03; //配置定时器0分频值
- TCNTB0 = 3125; //计数缓存寄存器
- TCMPB0 = 0; //比较缓存寄存器
- TCON |= (1<<1); //手动更新位且配置反相器位(开/关)(手动更行位将TCNTBn和TCMPBn的值分别给TCNTn和TCMPn寄存器)
- TCON = 0x09; //定时器使能并自动重载
- }
复制代码
► 定时器报警实验
♥ 实验设备
硬件:
PC机
一台
YX-AIO嵌入式综合创新设计平台
一台
ARM9核心板
一块
软件:
Windows操作系统,ADS1.2集成开发环境,H-JTAG下载环境
♥ 实验内容
使用定时器延时方式控制YX-AIO嵌入式综合创新设计平台上的蜂鸣器报警。
♥ 实验步骤
① 在ADS开发环境中使用ARM9_S3C2440工程模版创建一个工程,并将程序代码添加到该工程;
② 在ADS开发环境中编译链接并生成目标文件;
③ 将JTAG下载器连接YX-AIO嵌入式综合创新设计平台,并将该平台通电;
④ 开启H-JTAG软件检测处理器与NOR-Flash;
⑤ 使用H-JTAG软件将目标文件(.bin)下载到YX-AIO嵌入式综合创新设计平台上;
⑥ 复位平台。
源代码:
-
- void DelayMS(U32 dly)
- {
- U32 time_val;
-
- rTCFG0 = 3; //配置定时器0,1的预分频值
- rTCFG1 = 0<<4; //配置定时器1分频值
-
- time_val = PCLK/ (3+1) / 2 / 1000 - 1; // 1ms = PCLK / prescaler / divider / 1000
-
- rTCNTB1 = time_val; //计数缓存寄存器
- rTCMPB1 = time_val>>1; // 50%
-
- rTCON &= ~(0xf<<8); //手动更新位且配置反相器位(开/关)
- rTCON |= 0xb<<8; //定时器使能并自动重载
- rTCON &= ~(2<<8); //clear manual update bit
-
- while(dly--)
- {
- while(rTCNTO1>=time_val>>1);
- while(rTCNTO1>1);
- }
- }
-
- void Main(void)
- {
- //----------------添加自己的代码-------------------
- rGPACON &= ~(1<<16);
-
- while (1)
- {
- rGPADAT |= (1<<16);
- DelayMS(1000);
- rGPADAT &= ~(1<<16);
- DelayMS(500);
- }
- }