历史上的今天
返回首页

历史上的今天

今天是:2025年10月27日(星期一)

正在发生

2022年10月27日 | 【JZ2440笔记】定时器

2022-10-27 来源:csdn

一、前言

定时器可以说是任何单片机中的标配外设了,学过那么多种MCU,定时器模块非常简单,几乎都是一样的工作流程。用一个寄存器不断的计数来标记经过的时间,这个计数寄存器溢出后可以触发中断等事件,定时器模块一般都捆绑有PWM功能,就是再加一个比较寄存器,当比较寄存器中的值与计数器值相等时改变IO的电平,实现PWM控制。当然,S3C2440定时器模块也是一样的。


二、实验目标

采用定时器0实现定时器中断,每隔1秒改变1次开发板上LED的亮灭状态。


三、分析

S3C2440定时器模块方框图如下:

S3C2440A 有 5 个 16 位定时器。其中定时器 0、1、2 和 3 具有脉宽调制(PWM)功能。定时器 4 是一个无输出引脚的内部定时器。定时器 0 还包含用于大电流驱动的死区发生器。定时器 0 和 1 共用一个 8 位预分频器,定时器 2、3 和 4 共用另外的 8 位预分频器。每个定时器都有一个可以生成 5 种不同分频信号(1/2,1/4,1/8,1/16 和 TCLK)的时钟分频器。每个定时器模块从相应 8 位预分频器得到时钟的时钟分频器中得到其自己的时钟信号。8 位预分频器是可编程的,并且按存储在 TCFG0 和 TCFG1 寄存器中的加载值来分频 PCLK。定时计数缓冲寄存器(TCNTBn)包含了一个当使能了定时器时的被加载到递减计数器中的初始值。定时比较缓冲寄存器(TCMPBn)包含了一个被加载到比较寄存器中的与递减计数器相比较的初始值。这种 TCNTBn 和TCMPBn 的双缓冲特征保证了改变频率和占空比时定时器产生稳定的输出。


每个定时器有它自己的由定时器时钟驱动的 16 位递减计数器。当递减计数器到达零时,产生定时器中断请求通知 CPU 定时器操作已经完成。当定时器计数器到达零时,相应的 TCNTBn 的值将自动被加载到递减计数器以继续下一次操作。然而,如果定时器停止了,例如,在定时器运行模式期间清除 TCONn 的定时器使能位,TCNTBn的值将不会被重新加载到计数器中。


TCMPBn 的值是用于脉宽调制(PWM)。当递减计数器的值与定时器控制逻辑中的比较寄存器的值相匹配时定时器控制逻辑改变输出电平。因此,比较寄存器决定 PWM 输出的开启时间(或关闭时间)。


TCFG0设置对PCLK的分频:

TCFG1设置MUX的分频:

TCON可以设置定时器的开、关、使能自动重装载等功能。

设置TCON的时候有个注意点,就是要先设置手动更新位,然后再清除手动更新位。设置手动更新位的时候预装载值进入到TCNTO0,再清除手动更新位后下次定时器计数值到0后自动取TCNTB0中的值装载入TCNTO0中进行下一次递减计数。所以想要循环定时中断的话设置了手动更新位后要立刻再清除掉。


TCNTB0可以设置初始计数值,注意想要读取当前计数值不是读取TCNTB0,而是要去读取TCNTO0。

四、代码编写

代码分为以下几个文件:


head.S:启动文件。


main.c:各种C函数。


Makefile:编译代码。


文件内容分别如下:


head.S


@*************************************************************************

@ File:head.S

@*************************************************************************       

.text

.global _start

_start:

@******************************************************************************       

@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用

@******************************************************************************       

    b   Reset

@ 0x04: 未定义指令中止模式的向量地址

HandleUndef:

    b   HandleUndef 

 

@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式

HandleSWI:

    b   HandleSWI

@ 0x0c: 指令预取终止导致的异常的向量地址

HandlePrefetchAbort:

    b   HandlePrefetchAbort

@ 0x10: 数据访问终止导致的异常的向量地址

HandleDataAbort:

    b   HandleDataAbort

@ 0x14: 保留

HandleNotUsed:

    b   HandleNotUsed

@ 0x18: 中断模式的向量地址

    b   HandleIRQ

@ 0x1c: 快中断模式的向量地址

HandleFIQ:

    b   HandleFIQ

    

Reset: 

ldr     sp, =4096                       @设置堆栈,因为要调用C语言函数 

bl     disable_watch_dog               @关WATCH DOG

    bl      init_system_clk                 @初始化FCLK到400MHz,PCLK到50MHz

    msr cpsr_c, #0xd2       @ 进入中断模式

    ldr sp, =3072           @ 设置中断模式栈指针

    msr cpsr_c, #0xd3       @ 进入管理模式

    ldr sp, =4096           @ 设置管理模式栈指针,

                            @ 其实复位之后,CPU就处于管理模式,

                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略

    msr cpsr_c, #0x53       @ 设置I-bit=0,开IRQ中断

    ldr lr, =halt_loop      @ 设置返回地址

    ldr pc, =main           @ 调用main函数

halt_loop:

    b   halt_loop

 

HandleIRQ:

    sub lr, lr, #4                  @ 计算返回地址

    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器

                                    @ 注意,此时的sp是中断模式的sp

                                    @ 初始值是上面设置的3072

    

    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  

    ldr pc, =Timer0_Handle            @ 调用中断服务函数

int_return:

    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr


main.c


#define BYTE unsigned char

#define WORD unsigned short

#define DWORD unsigned int 

 

/* WOTCH DOG register */

#define REG_WTCON               (*(volatile unsigned long *)0x53000000)

 

/* Sys Clk Config */

#define REG_CLKDIVN             (*(volatile unsigned long *)0x4C000014)

#define REG_CAMDIVN             (*(volatile unsigned long *)0x4C000018)

#define REG_MPLLCON             (*(volatile unsigned long *)0x4C000004)

 

/* GPIO Configure*/

#define GPFCON (*(volatile unsigned long *)0x56000050)

#define GPFDAT (*(volatile unsigned long *)0x56000054)

#define GPFUP (*(volatile unsigned long *)0x56000058)

 

#define GPGCON (*(volatile unsigned long *)0x56000060)

#define GPGDAT (*(volatile unsigned long *)0x56000064)

#define GPGUP (*(volatile unsigned long *)0x56000068)

 

#define REG_EXTINT0 (*(volatile unsigned long *)0x56000088)

#define REG_EXTINT1 (*(volatile unsigned long *)0x5600008C)

 

/* interrupt Configure */

 

#define REG_EINTMASK (*(volatile unsigned long *)0x560000A4)

#define REG_INTMSK (*(volatile unsigned long *)0X4A000008)

#define REG_INTOFFSET (*(volatile unsigned long *)0x4A000014)

 

#define REG_EINTPEND (*(volatile unsigned long *)0x560000A8)

#define REG_SRCPND (*(volatile unsigned long *)0X4A000000)

#define REG_INTPND (*(volatile unsigned long *)0X4A000010)

 

// #define REG_INTSUBMSK (*(volatile unsigned long *)0x560000A8)

// #define REG_SUBSRCPND (*(volatile unsigned long *)0X4A000018)

 

/* timer Configure */

#define REG_TCFG0 (*(volatile unsigned long *)0x51000000)

#define REG_TCFG1 (*(volatile unsigned long *)0x51000004)

#define REG_TCON (*(volatile unsigned long *)0x51000008)

#define REG_TCNTB0 (*(volatile unsigned long *)0x5100000C)

 

void disable_watch_dog();

void init_system_clk();

void init_led();

void init_timer0();

void Timer0_Handle();

 

/*上电后,WATCH DOG默认是开着的,要把它关掉 */

void disable_watch_dog()

{

REG_WTCON = 0;

}

 

 

void init_system_clk()

{

    //HCLK = FCLK/4, 当 CAMDIVN[9] = 0 时

    //PCLK 设置为 HCLK/2 

    //完成配置FCLK : HCLK : PCLK = 1 : 1/4 : 1/8,DIVN_UPLL是USB的时钟不用管

    REG_CLKDIVN = (2 << 1) | (1 << 0);

 

    /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */

__asm__(

    "mrc    p15, 0, r1, c1, c0, 0n"        /* 读出控制寄存器 */ 

    "orr    r1, r1, #0xc0000000n"          /* 设置为“asynchronous bus mode” */

    "mcr    p15, 0, r1, c1, c0, 0n"        /* 写入控制寄存器 */

    );

 

    //m=MDIV+8, p=PDIV+2, s=SDIV, Mpll = ( 2 × m × Fin ) / ( p × 2^s )

    //FCLK = (2 * (92 + 8) * 12000000) / ((1 + 2) * 2) = 400000000 = 400MHz

    //配置完MPLL后时钟停振,CPU停止运行等待时钟输出稳定,之后FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */

    REG_MPLLCON = (92<<12)|(1<<4)|(1<<0);

}

 

void init_led()

{

GPFCON &= ~((DWORD)(3 << (2 * 4)) | (3 << (2 * 5)) | (3 << (2 * 6)));

GPFCON |= ((DWORD)(1 << (2 * 4)) | (1 << (2 * 5)) | (1 << (2 * 6))); //GPF4、GPF5、GPF6输出模式

 

GPFDAT |= (1 << 4) | (1 << 5) | (1 << 6);   //输出高电平,LED全灭

}

 

void init_timer0()

{

//Prescaler 0设置250分频

REG_TCFG0 &= ~((DWORD)0xFF << 0);

REG_TCFG0 |= ((DWORD)(250 - 1) << 0);

 

//MUX 0 设置4分频

REG_TCFG1 &= ~((DWORD)0xF << 0);

REG_TCFG1 |= ((DWORD)1 << 0);

 

//设置定时器0自动重装载值

REG_TCNTB0 = 50000;

 

//手动更新 TCNTB0 和 TCMPB0

REG_TCON |= ((DWORD)1 << 1);

//清除手动更新 TCNTB0 和 TCMPB0,不加这句定时器不运行

REG_TCON &= ~((DWORD)1 << 1);

 

//开定时器0中断

REG_INTMSK &= ~((DWORD)1 << 10);

 

//启动定时器0,使能自动重装载

REG_TCON |= ((DWORD)9 << 0);

}

 

void Timer0_Handle()

{

BYTE bIntOffset = REG_INTOFFSET;

 

switch(bIntOffset)

{

//定时器0中断

case 10:

GPFDAT ^= ((DWORD)1 << 4); //电平翻转

break;

 

default:

break;

}

 

//清中断

REG_SRCPND = (DWORD)1 << bIntOffset; 

REG_INTPND = REG_INTPND; 

}

 

int main()

{

init_led();

init_timer0();

 

while(1);

 

return 0;

}


Makefile


objs := head.o main.o

 

timer.bin: $(objs)

 

arm-linux-ld -Ttext 0x0000000 -g -o timer_elf $^

arm-linux-objcopy -O binary -S timer_elf $@

arm-linux-objdump -D -m arm timer_elf > timer.dis

%.o:%.c

arm-linux-gcc -Wall -O2 -c -o $@ $<

 

%.o:%.S

arm-linux-gcc -Wall -O2 -c -o $@ $<

 

clean:

rm -f timer.bin timer_elf timer.dis *.o


执行make命令后将生成的bin文件烧写到JZ2440开发板的NandFlash中,然后设置Nand启动,可以看到LED1每隔1秒改变1次亮灭状态。


五、实验总结

定时器的配置方式和寄存器基本都和单片机的一样,也就那些基本的操作。

推荐阅读

史海拾趣

Bomar公司的发展小趣事

90年代初,电子行业的竞争日益激烈,Bomar公司为了保持市场领先地位,开始寻求与全球优秀制造商的合作。在这一背景下,公司与一些精选的亚洲工厂建立了密切的合作关系。这些工厂按照Bomar公司的规格和标准进行生产,确保了产品质量的稳定性和一致性。通过与亚洲工厂的合作,Bomar公司成功降低了生产成本,提高了生产效率,进一步巩固了其在全球市场的地位。

Elcos AG公司的发展小趣事

随着全球对环保和可持续发展的重视,Elcos AG也开始注重自身的环保责任和可持续发展战略。公司采用环保材料和生产工艺,减少了对环境的污染和资源的浪费。同时,Elcos AG还积极参与社会公益事业和环保活动,通过捐款、赞助和志愿服务等方式回馈社会。这些举措不仅提升了公司的社会形象和品牌价值,也为公司的长期发展奠定了坚实的基础。

Analog Microelectronics GmbH公司的发展小趣事

随着国内市场的饱和,Elcos AG开始寻求国际化拓展的机会。公司首先在欧洲市场建立了销售网络,通过与当地合作伙伴的紧密合作,逐渐打开了欧洲市场的大门。随后,Elcos AG又将目光投向了亚洲和北美市场,通过设立海外办事处和参加国际展会等方式,积极推广公司品牌和产品。在国际市场的竞争中,Elcos AG凭借其卓越的产品品质和完善的售后服务体系,赢得了众多客户的信赖和支持。

意瑞(COSEMITECH)公司的发展小趣事

随着技术的不断成熟和产品的不断优化,意瑞半导体的市场影响力逐渐扩大。其高集成度、高性能的芯片广泛应用于国内外主流OEM及全球知名Tier1厂商。在汽车电子领域,公司布局了动力总成、车身控制、电源管理和底盘安全等模块,其产品在多家主机厂成功导入并量产,性能超过了国外老牌芯片,成为该品类中本土鲜有的实现汽车市场大批量出货的产品。

Amphenol Nexus公司的发展小趣事

2008年,Amphenol公司看中了Nexus, Inc.在连接器领域的潜力,决定对其进行收购。收购完成后,Amphenol成立了提供全球销售支持的Amphenol Nexus Technologies,同时仍保持了对客户支持和开发的坚定承诺。这一举措使Amphenol Nexus Technologies得以借助Amphenol的全球性的资源和网络,进一步拓展其业务范围和市场影响力。

Densitron公司的发展小趣事

在电子行业的激烈竞争中,Densitron公司始终坚持技术创新作为发展的核心动力。早期,公司投入大量研发资源,成功开发出一种新型的铟锡氧化物(ITO)材料,这种材料在触摸屏领域具有出色的性能,使得公司的产品在市场上脱颖而出。随着技术的不断升级,Densitron公司又相继推出了一系列创新产品,不仅满足了客户日益增长的需求,也推动了整个行业的进步。

问答坊 | AI 解惑

汽车安全与维修~~有了问题,到这里来找找答案吧!

1. 汽车气囊注意事项 >> https://bbs.eeworld.com.cn/thread-715-1-51.html 2. 汽车转速传感器的维修 >>https://bbs.eeworld.com.cn/thread-455-1-51.html 3. 解析奥迪防盗器7位密码的秘密>>https://bbs.eeworld.com.cn/thread-432-1-50.html 4. ...…

查看全部问答>

求职英语(一):个人品质英语词汇大全

个人品质有用词汇able 有才干的,能干的 active 主动的,活跃的 adaptable 适应性强的 adroit 灵巧的,机敏的 aggressive 有进取心的 alert 机灵的 ambitious 有雄心壮志的 amiable 和蔼可亲的 amicable 友好的 analytical 善于分析的 ap ...…

查看全部问答>

单片机时钟电路中的数码管驱动电路

.系统板上硬件连线 (1. 把“单片机系统”区域中的P1.0-P1.7端口用8芯排线连接到“动态数码显示”区域中的A-H端口上; (2. 把“单片机系统:区域中的P3.0-P3.7端口用8芯排线连接到“动态数码显示”区域中的S1-S8端口上; 动态数码 ...…

查看全部问答>

开关电源中的电感

饱和电感是一种磁滞回线矩形比高,起始磁导率高,矫顽力小,具有明显磁饱和点的电感,在电子电路中常被当作可控延时开关元件来使用。由于其独特的物理特性,使之在高频开关电源的开关噪声抑制,大电流输出辅路稳压,移相全桥变换器,谐振变换器及逆 ...…

查看全部问答>

关于模电数电的问题

最近在疯狂学习模电数电,感觉数电还可以,就是逻辑设计。但是模电实在太让我头大了,各种放大电路太抽象了,原理倒是能搞懂,只是到底是干嘛的根本不清楚,还要用各种分析方法去分析,根本记不住。请教各位大大,各位学习模电数电的时候是如何学习 ...…

查看全部问答>

如何精确线程循环周期

有个线程 while(1) {     f1();     Sleep(100); } 本来初衷是使这个线程每100毫秒循环一次,但是由于Sleep函数的不精确,以及f1()的执行时间不确定,因此,次线程循环周期总是不能精确到100毫秒,有没有什么方法能使此线 ...…

查看全部问答>

求教:设备意外删除后收不到IRP_MN_REMOVAL_DEVICE?

为什么SURPRISE_REMOVE后没有REMOVE_DEVICE 小弟写的一个USB驱动程序在安装完成之后,插入设备,设备正常启动,然后直接拔出设备,有时候会出现收不到IRP_MN_REMOVAL_DEVICE的现象。 在log中,IRP序列为: 1)IRP_MN_QUERY_DEVICE_RELATION 连续 ...…

查看全部问答>

MC-1000中,如何对背光进行控制

在开发MC-1000中,想在程序一开启就把背光灯打开,用什么函数来控制打开背光呢,各位老大,请赐教。在线等。。。急。我用的时evc4.0开发平台。…

查看全部问答>

LM3S9B92烧写不成功及FLASH烧写次数限制

请问9b92 C1版本的FLASH是不是有烧写限制?是多少次?我在网上看到一些说是的100次左右,这说法是否正确? 同时能否解释下下面的问题?谢谢~~~ 我下载时提示“Could not power up debug port: Control/Status register reads 000000F0” 用JLINK ...…

查看全部问答>

430也能玩嵌入式:Contiki内核在IAR+MSP430下移植(八)

四、其他地方(1)loader-arch.hcpu/msp430/loader-arch.c文件中的#include \"loader/loader-arch.h\"改成#include \"loader/elfloader-arch.h\"。(通过逻辑判断)(2)FSSEL_SMCLKcontiki-2.5/cpu/msp430/rom.c文件中的FCTL2 = FWKEY | FSSEL_SMCLK | ( ...…

查看全部问答>