S5PV210时钟和定时器应用实例

来源:本站
导读:目前正在解读《S5PV210时钟和定时器应用实例》的相关信息,《S5PV210时钟和定时器应用实例》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《S5PV210时钟和定时器应用实例》的详细说明。
简介:这篇文章主要讲解了S5PV210时钟和定时器应用实例,文章通过程序启动时钟、定时器等方面进行讲解。

1.启动程序start.S

启动程序相关代码与前一章中断的启动程序共用,所以这里不再重复讲解。

2.系统时钟初始化和打印各时钟的频率

(1)系统时钟初始化

01 #include "uart.h"

02

03 #define APLL_LOCK*((volatile unsigned int *)0xE0100000)

04 #define MPLL_LOCK*((volatile unsigned int *)0xE0100008)

05 #define EPLL_LOCK*((volatile unsigned int *)0xE0100010)

06#define VPLL_LOCK*((volatile unsigned int *)0xE0100020)

07

08 #define APLL_CON0*((volatile unsigned int *)0xE0100100)

09 #define MPLL_CON*((volatile unsigned int *)0xE0100108)

10 #define EPLL_CON0*((volatile unsigned int *)0xE0100110)

11 #define VPLL_CON*((volatile unsigned int *)0xE0100120)

12

13 #define CLK_SRC0*((volatile unsigned int *)0xE0100200)

14 #define CLK_SRC4*((volatile unsigned int *)0xE0100210)

15

16 #define CLK_DIV0*((volatile unsigned int *)0xE0100300)

17 #define CLK_DIV4*((volatile unsigned int *)0xE0100310)

18 /*设置目标时钟频率(手册推荐值):

19* ARMCLK=1000MHz, HCLK_MSYS=200MHz, PCLK_MSYS=100MHz

20* PCLK_PSYS =66.7MHz, HCLK_DSYS=166.75MHz, PCLK_DSYS=83.375MHz,

21* HCLK_PSYS =133.44MHz

22*/

23 void clock_init(void)

24 {

25/* 1.设置PLL锁定值(默认值可以不设置) */

26APLL_LOCK = 0x0FFF;

27MPLL_LOCK = 0x0FFF;

28EPLL_LOCK = 0x0FFF;

29VPLL_LOCK = 0x0FFF;

30/* 2.设置PLL的PMS值(使用手册推荐值),并使能PLL

31*PMSEN*/

32APLL_CON0= (3 <<8)|(125<<16)|(1<<0)|(1<<31);/* FOUT_APLL = 1000MHz */

33MPLL_CON= (12<<8)|(667<<16)|(1<<0)|(1<<31);/* FOUT_MPLL = 667MHz */

34EPLL_CON0 = (3<<8) |(48 <<16)|(2<<0)|(1<<31);/* FOUT_EPLL = 96MHz */

35VPLL_CON= (6<<8) |(108<<16)|(3<<0)|(1<<31);/* FOUT_VPLL = 54MHz */

36/* 3.等待PLL锁定*/

37while (!(APLL_CON0 & (1<<29)));

38while (!(MPLL_CON& (1<<29)));

39while (!(EPLL_CON0 & (1<<29)));

40while (!(VPLL_CON& (1<<29)));

41/* 4.时钟源的设置

42* APLL_SEL[0] :1 = FOUTAPLL

43* MPLL_SEL[4] :1 = FOUTMPLL

44* EPLL_SEL[8] :1 = FOUTEPLL

45* VPLL_SEL[12]:1 = FOUTVPLL

46* MUX_MSYS_SEL[16]:0 = SCLKAPLL

47* MUX_DSYS_SEL[20]:0 = SCLKMPLL

48* MUX_PSYS_SEL[24]:0 = SCLKMPLL

49* ONENAND_SEL [28]:0 = HCLK_PSYS

50*

51* MOUT_MSYS=FOUT_APLL=1000MHz

52* MOUT_DSYS=FOUT_MPLL=667MHz

53* MOUT_PSYS=FOUT_MPLL=667MHz

54*/

55CLK_SRC0 = (1<<0)|(1<<4)|(1<<8)|(1<<12);

56/* 5.设置其他模块的时钟源,CLK_SRC1~6

57*在实际嵌入式系统开发中,还需要为其他功能模块配置时钟,比如UART0

58* MOUTUART0=SCLKMPLL

59*/

60CLK_SRC4 = (6<<16);

61/* 6.设置分频系数

62* APLL_RATIO[2:0]: APLL_RATIO = 0x0 freq(ARMCLK) = MOUT_MSYS / (APLL_RATIO + 1) = 1000MHz

63* A2M_RATIO [6:4]: A2M_RATIO= 0x4 freq(A2M) = SCLKAPLL / (A2M_RATIO + 1) = 200MHz

64* HCLK_MSYS_RATIO[10:8]: HCLK_MSYS_RATIO = 0x4 freq(HCLK_MSYS) = ARMCLK / (HCLK_MSYS_RATIO + 1) = 200MHz

65* PCLK_MSYS_RATIO[14:12]:PCLK_MSYS_RATIO = 0x1 freq(PCLK_MSYS) = HCLK_MSYS / (PCLK_MSYS_RATIO + 1) = 100MHz

66* HCLK_DSYS_RATIO[19:16]:HCLK_DSYS_RATIO = 0x3 freq(HCLK_DSYS) = MOUT_DSYS / (HCLK_DSYS_RATIO + 1) = 166.75MHz

67* PCLK_DSYS_RATIO[22:20]:PCLK_DSYS_RATIO = 0x1 freq(PCLK_DSYS) = HCLK_DSYS / (PCLK_DSYS_RATIO + 1) = 83.375MHz

68* HCLK_PSYS_RATIO[27:24]:HCLK_PSYS_RATIO = 0x4 freq(HCLK_PSYS) = MOUT_PSYS / (HCLK_PSYS_RATIO + 1) = 133.44MHz

69* PCLK_PSYS_RATIO[30:28]:PCLK_PSYS_RATIO = 0x1 freq(PCLK_PSYS) = HCLK_PSYS / (PCLK_PSYS_RATIO + 1) = 66.7MHz

70*/

71CLK_DIV0 = (1<<28)|(4<<24)|(1<<20)|(3<<16)|(1<<12)|(4<<8)|(4<<4);

72/* 7.设置其他模块的时钟分频值CLK_DIV1~7

73* UART0_RATIO = 0x9 SCLK_UART0=MOUTUART0/(UART0_RATIO + 1)=667/(9+1)=66.7MHz

74*/

75CLK_DIV4 = (9<<16);

76 }

(2)打印各时钟的频率值

01 /*计算x的y次方*/

02 volatile unsigned int pow(volatile unsigned int x, volatile unsigned char y)

03 {

04if (y == 0)

05x = 1;

06else

07{

08y--;

09while (y--)

10x *= x;

11}

12return x;

13 }

14 void raise(int signum)

15 {

16 }

17 /*打印时钟信息

18*外部晶振FINPLL=24MHz*/

19 void print_clockinfo(void)

20 {

21volatile unsigned short p, m, s, k;

22volatile unsigned int SCLKAPLL, SCLKMPLL, SCLKEPLL, SCLKVPLL, MHz;

23volatile unsigned int MOUT_MSYS, MOUT_DSYS, MOUT_PSYS, MOUT_UART0;

24volatile unsigned char APLL_RATIO, A2M_RATIO, HCLK_MSYS_RATIO, PCLK_MSYS_RATIO, HCLK_DSYS_RATIO,

25PCLK_DSYS_RATIO, HCLK_PSYS_RATIO, PCLK_PSYS_RATIO,UART0_RATIO;

26APLL_RATIO = (CLK_DIV0 >> 0)& 0x7;

27A2M_RATIO= (CLK_DIV0 >> 4) & 0x7;

28HCLK_MSYS_RATIO= (CLK_DIV0 >> 8) & 0x7;

29PCLK_MSYS_RATIO= (CLK_DIV0 >> 12) & 0x7;

30HCLK_DSYS_RATIO= (CLK_DIV0 >> 16) & 0x7;

31PCLK_DSYS_RATIO= (CLK_DIV0 >> 20) & 0x7;

32HCLK_PSYS_RATIO= (CLK_DIV0 >> 24) & 0x7;

33PCLK_PSYS_RATIO= (CLK_DIV0 >> 28) & 0x7;

34UART0_RATIO= (CLK_DIV4 >> 16) & 0xF;

35

36if (CLK_SRC0 & 0x1)

37{

38p = (APLL_CON0 >> 8)& 0x3F;

39m = (APLL_CON0 >> 16) & 0x3FF;

40s = (APLL_CON0 >> 0)& 0x7;

41SCLKAPLL = m * 24 / (p * pow(2, s - 1));/* FOUT_APLL = MDIV X FIN / (PDIV ×2^(SDIV-1)) */

42}

43else

44SCLKAPLL = 24;

45

46if (CLK_SRC0 & (1 << 4))

47{

48p = (MPLL_CON >> 8)& 0x3F;

49m = (MPLL_CON >> 16) & 0x3FF;

50s = (MPLL_CON >> 0)& 0x7;

51SCLKMPLL = m * 24 / (p * pow(2, s));/* FOUT_MPLL = MDIV X FIN / (PDIV ×2^SDIV) */

52}

53else

54SCLKMPLL = 24;

55

56if (CLK_SRC0 & (1 << 8))

57{

58p = (EPLL_CON0 >> 8)& 0x3F;

59m = (EPLL_CON0 >> 16) & 0x1FF;

60s = (EPLL_CON0 >> 0)& 0x7;

61k = EPLL_CON1;

62SCLKEPLL = (m + k / 65536) * 24 / (p * pow(2, s));/* FOUT_EPLL = (MDIV + K / 65536) x FIN / (PDIV x 2^SDIV) */

63}

64else

65SCLKEPLL = 24;

66

67if (CLK_SRC0 & (1 << 12))

68{

69p = (VPLL_CON >> 8)& 0x3F;

70m = (VPLL_CON >> 16) & 0x1FF;

71s = (VPLL_CON >> 0)& 0x7;

72SCLKVPLL = m * 24 / (p * pow(2, s));/* FOUT_VPLL = MDIV X FIN / (PDIV ×2^SDIV) */

73}

74else

75SCLKVPLL = 24;

76

77if (CLK_SRC0 & (1 << 16))

78MOUT_MSYS = SCLKMPLL;

79else

80MOUT_MSYS = SCLKAPLL;

81

82if (CLK_SRC0 & (1 << 20))

83MOUT_DSYS = SCLKAPLL / (A2M_RATIO + 1);

84else

85MOUT_DSYS = SCLKMPLL;

86

87if (CLK_SRC0 & (1 << 24))

88MOUT_PSYS = SCLKAPLL / (A2M_RATIO + 1);

89else

90MOUT_PSYS = SCLKMPLL;

91

92if ( (((CLK_SRC4 & (6 << 16))>>16)&0xF) == 0x6)

93MOUT_UART0 = SCLKMPLL;

94else

95MOUT_UART0 = 66;

96

97MHz = MOUT_MSYS / (APLL_RATIO + 1);

98printf("ARMCLK= %d MHzrn", MHz);

99MHz = SCLKAPLL / (A2M_RATIO + 1);

100printf("SCLKA2M= %d MHzrn", MHz);

101MHz /= (HCLK_MSYS_RATIO + 1);

102printf("HCLK_MSYS= %d MHzrn", MHz);

103MHz /= (PCLK_MSYS_RATIO + 1);

104printf("PCLK_MSYS= %d MHzrn", MHz);

105MHz = MOUT_DSYS / (HCLK_DSYS_RATIO + 1);

106printf("HCLK_DSYS= %d MHzrn", MHz);

107MHz /= (PCLK_DSYS_RATIO + 1);

108printf("PCLK_DSYS= %d MHzrn", MHz);

109MHz = MOUT_PSYS / (HCLK_PSYS_RATIO + 1);

110printf("HCLK_PSYS= %d MHzrn", MHz);

111printf("PCLK_PSYS= %d MHzrn", MHz /(PCLK_PSYS_RATIO + 1));

112printf("SCLKEPLL= %d MHzrn", SCLKEPLL);

113MHz = MOUT_UART0 / (UART0_RATIO + 1);

114printf("SCLK_UART0= %d MHzrn", MHz);

115 }

第01~13行用于计算一个数x的y次方根;第14~16行是一个空函数raise,这个函数是为编译器所定义的,在后面Makefile部分再作详细介绍;第17~115行用于打印系统时钟的频率,其中第26~35行从CLK_DIV0和CLK_DIV4寄存器中读取各时钟的分频系数值,第36~96行用于计算各时钟源的频率,第97~115行将计算出来的时钟频率通过串口输出,这里面用到了浮点除法运算。

3.定时器Timer0初始化

01 #defineTCFG0(*(volatile unsigned int *)0xE2500000)

02 #defineTCFG1(*(volatile unsigned int *)0xE2500004)

03 #defineTCON(*(volatile unsigned int *)0xE2500008)

04 #defineTCNTB0(*(volatile unsigned int *)0xE250000C)

05 #defineTCMPB0(*(volatile unsigned int *)0xE2500010)

06/*

07* Timer input clock Frequency = PCLK / {prescaler value+1} / {pider value}

08* {prescaler value} = 1~255

09* {pider value} = 1, 2, 4, 8, 16, TCLK

10*本实验的Timer0的时钟频率=66.7MHz/(65+1)/(16)=63162Hz(即1s计数63162次)

11*设置Timer0 1秒钟触发一次中断:

12*/

13 void timer0_init(void)

14 {

15TCNTB0 = 63162;/* 1秒钟触发一次中断*/

16TCMPB0 = 31581;/* PWM占空比=50% */

17TCFG0 |= 65;/* timer 0 Prescaler value = 65 */

18TCFG1= 0x04;/*选择16分频*/

19TCON|= (1<<1);/*手动更新*/

20TCON= 0x09;/*自动加载,清“手动更新”位,启动定时器0 */

21 }

4.定时器中断初始化

01 #define TINT_CSTAT*((volatile unsigned int *)0xE2500044)

02

03 #defineVIC0IRQSTATUS*((volatile unsigned int *)0xF2000000)

04 #define VIC0INTSELECT*((volatile unsigned int *)0xF200000C)

05 #define VIC0INTENABLE*((volatile unsigned int *)0xF2000010)

06 #define VIC0VECTADDR21*((volatile unsigned int *)0xF2000154)

07 #define VIC0ADDRESS*((volatile unsigned int *)0xF2000F00)

08

09 extern void IRQ_handle(void);

10

11 //使能TIMER0中断

12 void init_irq(void)

13 {

14TINT_CSTAT |= 1;

15 }

16 //清中断

17 void clear_irq(void)

18 {

19TINT_CSTAT |= (1<<5);

20 }

21 //初始化中断控制器

22 void init_int(void)

23 {

24//选择中断类型为IRQ

25VIC0INTSELECT |= ~(1<<21); // TIMER0中断为IRQ

26//清VIC0ADDRESS

27VIC0ADDRESS = 0x0;

28//设置TIMER0中断对应的中断服务程序的入口地址

29VIC0VECTADDR21 = (int)IRQ_handle;

30//使能TIMER0中断

31VIC0INTENABLE |= (1<<21);

32 }

33 //清除需要处理的中断的中断处理函数的地址

34 void clear_vectaddr(void)

35 {

36VIC0ADDRESS = 0x0;

37 }

38 //读中断状态

39 unsigned long get_irqstatus(void)

40 {

41return VIC0IRQSTATUS;

42 }

5.格式化输出printf实现

在实验中需要通过串口输出时钟频率信息,需要用到C库中的printf函数,但不想调用此库,所以我们需要实现printf这个函数,代码如下。

/*打印整数v到终端*/

01 void put_int(volatile unsigned int v)

02 {

03int i;

04volatile unsigned char a[10];

05volatile unsigned char cnt = 0;

06

07if (v == 0)

08{

09putc('0');

10return;

11}

12

13while (v)

14{

15a[cnt++] = v % 10;

16v /= 10;

17}

18

19for (i = cnt - 1; i >= 0; i--)

20putc(a[i] + 0x30); //整数0-9的ASCII分别为0x30-0x39

21 }

22 /*格式化输出到终端*/

24 23 int printf(const char *fmt, ...)

25 {

26va_list ap;

27char c;

28char *s;

29volatile unsigned int d;

30volatile unsigned char lower;

31

32va_start(ap, fmt);

33while (*fmt)

34{

35lower = 0;

36c = *fmt++;

37if (c == '%')

38{

39switch (*fmt++)

40{

41case 'c':/* char */

42c = (char) va_arg(ap, int);

43putc(c);

44break;

45case 's':/* string */

46s = va_arg(ap, char *);

47puts(s);

48break;

49case 'd':/* int */

50case 'u':

51d = va_arg(ap, int);

52put_int(d);

53break;

54}

55}

56else

57putc(c);

58}

59va_end(ap);

60 }

以上printf只支持单字符型、字符串、整型的格式化输出,其他格式的输出需要时再补充。

6.中断服务程序和主程序

(1)中断服务程序

01 // TIMER0中断服务程序(ISR)

02 void irq_handler()

03 {

04volatile unsigned char status = ((get_irqstatus() & (1<<21))>>21)&0x1;// TIMER0's int status

05clear_vectaddr();/*清中断向量寄存器*/

06clear_irq();/*清timer0中断*/

07if (status == 0x1)

08{

09leds_on_off();

10}

11 }

(2)主程序

01 int main(void)

02 {

03int c = 0;

04

05init_leds();/*初始化GPIO引脚*/

06uart0_init();/*初始化UART0 */

07timer0_init();/*初始化Timer0 */

08

09puts("########## Before Init System Clock ##########rn");

10print_clockinfo();

11

12clock_init();/*初始化系统时钟*/

13uart0_init();/*初始化UART0 */

14

15puts("########## After Init System Clock ##########rn");

16print_clockinfo();

17

18init_irq();/*使能TIMER0中断*/

19init_int();/*初始化中断控制器、使能中断*/20

20

21while (1);

22 }

7.Makefile

01 objs := start.o main.o clock.o uart.o int.o timer.o

02 LDFLAGS=-lgcc -L/opt/tools/crosstool/arm-cortex_a8-linux-gnueabi/lib/gcc/arm-cortex_

a8-linux-gnueabi/4.4.6/

03

04 clock_timer.bin: $(objs)

05arm-linux-ld -Ttext 0xD0020010 -o clock_timer.elf $^ $(LDFLAGS)

06arm-linux-objcopy -O binary -S clock_timer.elf $@

07arm-linux-objdump -D clock_timer.elf > clock_timer.dis

08

09 %.o : %.c

10arm-linux-gcc -c -O2 -fno-builtin $< -o $@

11

12 %.o : %.S

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

14

15 clean:

16rm -f *.o *.elf *.bin *.dis

17

在本章的实验中,用到了浮点除法运算,而前面在制作交叉工具链时,我们选择的浮点类型是软浮点,并不是硬件浮点,所以如果在Makefile中没有将浮点运算相关的库包含进来编译,编译时会报如下错误:

undefined reference to `__aeabi_uip'

要解决这个编译错误,必须将浮点运算相关的库包含进来,对应的库为libgcc.a的静态库,在本书交叉工具链中,此库的路径为“/opt/tools/crosstool/arm-cortex_a8-linux-gnueabi/lib/

gcc/arm-cortex_a8-linux-gnueabi/4.4.6”,所以在链接时要将此库链接到ELF文件中,具体定义如上述Makefile所示。在Makefile中将浮点运算的库链接进来还是不够的,编译器还会去找一个名叫raise的函数,这个函数在Linux内核中是一个系统函数,但在uboot中是没有这个函数的,所以我们定义一个空的raise函数“欺骗”下编译器,这样我们就可以成功编译。最后在补充说明下,这样的编译问题在编译linux内核时是不是也会发生,这里说下是不会的,因为在内核中这些库事先都包含好了。

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4

/* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:宋体; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:宋体; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;}

将所有源代码传到宿主机上编译生成clock_timer.bin,将bin文件通过SD卡烧写到开发板上运行,可以看到LED1和LED2每隔1秒亮一次,这就是定时器中断的功能。另外通过串口输出系统时钟配置前后各自的频率值,如下所示:

########## Before Init System Clock ##########

ARMCLK= 400 MHz

SCLKA2M= 133 MHz

HCLK_MSYS= 44 MHz

PCLK_MSYS= 22 MHz

HCLK_DSYS= 133 MHz

PCLK_DSYS= 66 MHz

HCLK_PSYS= 133 MHz

PCLK_PSYS= 66 MHz

SCLKEPLL= 40 MHz

SCLK_UART0= 66 MHz

########## After Init System Clock ##########

ARMCLK= 1000 MHz

SCLKA2M= 200 MHz

HCLK_MSYS= 40 MHz

PCLK_MSYS= 20 MHz

HCLK_DSYS= 166 MHz

PCLK_DSYS= 83 MHz

HCLK_PSYS= 133 MHz

PCLK_PSYS= 66 MHz

SCLKEPLL= 96 MHz

SCLK_UART0= 66 MHz

其中配置前的时钟是系统默认的时钟,即iROM中相关的程序设置的时钟频率,配置后的时钟是本书参考S5PV210手册推荐的频率值设置的,有兴趣读者可以配置其他的时钟频率试试,不过不要超出手册上建议的范围即可。

/* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:宋体; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:宋体; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;}

提醒:《S5PV210时钟和定时器应用实例》最后刷新时间 2024-03-14 00:59:15,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《S5PV210时钟和定时器应用实例》该内容的真实性请自行鉴别。