IMX6UL裸机实现C语言LED点亮与闪烁

2019-11-26 13:47发布

C 语言部分有两个文件 main.c 和 main.h,main.h 里面主要是定义的寄存器地址,在 main.h里面输入代码:

#ifndef MAIN_H 

#define MAIN_H

1 /*

2 * CCM 相关寄存器地址

3 */

4 #define CCM_CCGR0 *((volatile unsigned int *)0X020C4068)

5 #define CCM_CCGR1 *((volatile unsigned int *)0X020C406C)

6 #define CCM_CCGR2 *((volatile unsigned int *)0X020C4070)

7 #define CCM_CCGR3 *((volatile unsigned int *)0X020C4074)

8 #define CCM_CCGR4 *((volatile unsigned int *)0X020C4078)

9 #define CCM_CCGR5 *((volatile unsigned int *)0X020C407C)

10 #define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)

11

12 /*

13 * IOMUX 相关寄存器地址

14 */

15 #define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)

16 #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)

17

18 /*

19 * GPIO1 相关寄存器地址

20 */

21 #define GPIO1_DR *((volatile unsigned int *)0X0209C000)

22 #define GPIO1_GDIR *((volatile unsigned int *)0X0209C004)

23 #define GPIO1_PSR *((volatile unsigned int *)0X0209C008)

24 #define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C)

25 #define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010)

26 #define GPIO1_IMR *((volatile unsigned int *)0X0209C014)

27 #define GPIO1_ISR *((volatile unsigned int *)0X0209C018)

28 #define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)

29

30 #endif

在 main.h 中我们以宏定义的形式定义了要使用到的所有寄存器,后面的数字就是其地址,比如CCM_CCGR0 寄存器的地址就是 0X020C4068,这个很简单,很好理解。接下看一下 main.c 文件,在 main.c 里面输入如下所示代码:

1 #include "main.h" 

2

3 /*

4 * @description : 使能 I.MX6U 所有外设时钟

5 * @param : 无

6 * @return : 无

7 */

8 void clk_enable(void) 9 {

10 CCM_CCGR0 = 0xffffffff;

11 CCM_CCGR1 = 0xffffffff;

12 CCM_CCGR2 = 0xffffffff;

13 CCM_CCGR3 = 0xffffffff;

14 CCM_CCGR4 = 0xffffffff;

15 CCM_CCGR5 = 0xffffffff;

16 CCM_CCGR6 = 0xffffffff; 17 }

18

19 /*

20 * @description : 初始化 LED 对应的 GPIO

21 * @param : 无

22 * @return : 无

23 */

24 void led_init(void) 25 {

26 /* 1、初始化 IO 复用, 复用为 GPIO1_IO03 */

27 SW_MUX_GPIO1_IO03 = 0x5; 28

29 /* 2、配置 GPIO1_IO03 的 IO 属性

30 *bit 16:0 HYS 关闭

31 *bit [15:14]: 00 默认下拉

32 *bit [13]: 0 kepper 功能

33 *bit [12]: 1 pull/keeper 使能

34 *bit [11]: 0 关闭开路输出

35 *bit [7:6]: 10 速度 100Mhz

36 *bit [5:3]: 110 R0/6 驱动能力

37 *bit [0]: 0 低转换率

38 */

39 SW_PAD_GPIO1_IO03 = 0X10B0;

40

41 /* 3、初始化 GPIO, GPIO1_IO03 设置为输出 */ 42 GPIO1_GDIR = 0X0000008;

43

44 /* 4、设置 GPIO1_IO03 输出低电平,打开 LED0 */

45 GPIO1_DR = 0X0; 46 }

47

48 /*

49 * @description : 打开 LED 灯

50 * @param : 无

51 * @return : 无

52 */

53 void led_on(void) 54 {

55 /*

56 * 将 GPIO1_DR 的 bit3 清零

57 */

58 GPIO1_DR &= ~(1<<3);

59 }

60

61 /*

62 * @description : 关闭 LED 灯

63 * @param : 无

64 * @return : 无

65 */

66 void led_off(void) 67 {

68 /*

69 * 将 GPIO1_DR 的 bit3 置 1 70 */

71 GPIO1_DR |= (1<<3);

72 }

73

74 /*

75 * @description : 短时间延时函数

76 * @param - n : 要延时循环次数(空操作循环次数,模式延时)

77 * @return : 无

78 */

79 void delay_short(volatile unsigned int n) 80 {

81 while(n--){}

82 }

83

84 /*

85 * @description : 延时函数,在 396Mhz 的主频下延时时间大约为 1ms

86 * @param - n : 要延时的 ms 数

87 * @return : 无

88 */

89 void delay(volatile unsigned int n) 90 {

91 while(n--)

92 {

93 delay_short(0x7ff); 94 }

95 }

96

97 /*

98 * @description : mian 函数

99 * @param : 无

100 * @return : 无

101 */

102 int main(void)

103 {

104 clk_enable(); /* 使能所有的时钟 */

105 led_init(); /* 初始化 led */ 106

107 while(1) /* 死循环 */

108 {

109 led_off(); /* 关闭 LED */ 110 delay(500); /* 延时大约 500ms */ 111

112 led_on(); /* 打开 LED */ 113 delay(500); /* 延时大约 500ms */

114 }

115

116 return 0;

117 }

main.c 文件里面一共有 7 个函数, 这 7 个函数都很简单。clk_enable 函数是使能CCGR0~CCGR6 所控制的所有外设时钟。led_init 函数是初始化 LED 灯所使用的 IO,包括设置IO 的复用功能、IO 的属性配置和 GPIO 功能,最终控制 GPIO 输出低电平来打开 LED 灯。

led_on 和 led_off 这两个函数看名字就知道,用来控制 LED 灯的亮灭的。delay_short()和 delay()这两个函数是延时函数,delay_short()函数是靠空循环来实现延时的,delay()是对 delay_short()的简单封装, 在 I.MX6U 工作在 396MHz(Boot ROM 设置的 396MHz) 的主频的时候delay_short(0x7ff)基本能够实现大约 1ms 的延时,所以 delay()函数我们可以用来完成 ms 延时。main 函数就是我们的主函数了,在 main 函数中先调用函数 clk_enable()和 led_init()来完成时钟使能和 LED 初始化,最终在 while(1)循环中实现 LED 循环亮灭,亮灭时间大约是 500ms。本实验的程序部分就是这些了,接下来即使编译和测试了。

新建 Makefile 文件,在 Makefile 文件里面输入如下内容:

1 objs := start.o main.o

2

3 ledc.bin:$(objs)

4 arm-linux-gnueabihf-ld -T0X87800000 -o ledc.elf $^

5 arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@

6 arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis 7

8 %.o:%.s

9 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $< 10

11 %.o:%.S

12 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<

13

14 %.o:%.c

15 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<

16

17 clean:

18 rm -rf *.o ledc.bin ledc.elf ledc.dis

上述的 Makefile里面用到了 Makefile 变量和自动变量,关于 Makefile 的变量和自动变量的请参考《Linux下Makefile文件的模式规则和自动化变量》。

第 1 行定义了一个变量 objs,objs 包含着要生成 ledc.bin 所需的材料:start.o 和main.o,也就是当前工程下的 start.s 和 main.c 这两个文件编译后的.o 文件。这里要注意 start.o 一定要放到最前面!因为在后面链接的时候 start.o 要在最前面,因为 start.o 是最先要执行的文件!

第 3 行就是默认目标,目的是生成最终的可执行文件 ledc.bin,ledc.bin 依赖 start.o 和 main.o如果当前工程没有 start.o 和 main.o 的时候就会找到相应的规则去生成 start.o 和 main.o。比如start.o 是 start.s 文件编译生成的,因此会执行第 8 行的规则。

第 4 行是使用 arm-linux-gnueabihf-ld 进行链接,链接起始地址是 0X87800000,但是这一行用刀了自动变量“$^”,“$^”的意思是所有依赖文件的集合,在这里就是 objs 这个变量的值:

start.o 和 main.o。链接的时候 start.o 要链接到最前面,因为第一行代码就是 start.o 里面的,因此这一行就相当于:

arm-linux-gnueabihf-ld -T0X87800000 -o ledc.elf start.o main.o

第 5 行使用 arm-linux-gnueabihf-objcopy 来将 ledc.elf 文件转为 ledc.bin,本行也用到了自动变量“$@”,“$@”的意思是目标集合,在这里就是“ledc.bin”,那么本行就相当于:

arm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.bin

第 6 行使用 arm-linux-gnueabihf-objdump 来反汇编,生成 ledc.dis 文件。

第 8~15 行就是针对不同的文件类型将其编译成对应的.o 文件,其实就是汇编.s(.S)和.c 文件,比如 start.s 就会使用第 8 行的规则来生成对应的 start.o 文件。第 9 行就是具体的命令,这行也用到了自动变量“$@”和“$<”,其中“$<”的意思是依赖目标集合的第一个文件。比如start.s 要编译成 start.o 的话第 8 行和第 9 行就相当于:

start.o:start.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o start.o start.s

第 17 行就是工程清理规则,通过命令“make clean”就可以清理工程。

Makefile 文件就讲到这里,我们可以将整个工程拿到 Ubuntu 下去编译,编译完成以后可以使用软件 imxdownload 将其下载到 SD 卡中,命令如下:

chmod 777 imxdownload //给予 imxdownoad 可执行权限,一次即可

./imxdownload ledc.bin /dev/sdd //下载到 SD 卡中

使用软件 imxdownload 将编译出来的ledc.bin 烧写到 SD 卡中,命令如下:

chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可

./imxdownload ledc.bin /dev/sdd //烧写到 SD 卡中

烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板,如果代码运行正常的话 LED0 就会以 500ms 的时间间隔亮灭。

5036dc041a674924a9c746f24ef25a54

LED0闪烁