引言
接下来开始详细编写实验代码,实现led功能。
4.2 硬件原理分析
本次笔记使用到的硬件资源和i之前笔记一样,就是一个 LED0
。
4.3 实验程序编写
创建 VSCode
工程,工作区名字为“
ledc_stm32
”,新建三个文件:
start.S
、
main.c
和
imx6ul.h
。其中 start.S
是汇编文件,
start.S
文件的内容和第十章的
start.S
一样,直接复制过来就可以。
main.c和 imx6ul.h
是
C
文件,完成以后如下图
所示:
文件 imx6ul.h
用来存放外设寄存器定义,在
imx6ul.h
中输入如下代码:
/*
*
外设寄存器组的基地址
*/
1
#define CCM_BASE
(
0X020C4000
)
2
#define CCM_ANALOG_BASE
(
0X020C8000
)
3
#define IOMUX_SW_MUX_BASE
(
0X020E0014
)
4
#define IOMUX_SW_PAD_BASE
(
0X020E0204
)
5
#define GPIO1_BASE
(
0x0209C000
)
6
#define GPIO2_BASE
(
0x020A0000
)
7
#define GPIO3_BASE
(
0x020A4000
)
8
#define GPIO4_BASE
(
0x020A8000
)
9
#define GPIO5_BASE
(
0x020AC000
)
10
11
/*
12
* CCM
寄存器结构体定义,分为
CCM
和
CCM_ANALOG
13
*/
14
typedef
struct
15
{
16
volatile unsigned int
CCR
;
17
volatile unsigned int
CCDR
;
18
volatile unsigned int
CSR
;
……
46
volatile unsigned int
CCGR6
;
47
volatile unsigned int
RESERVED_3
[
1
];
48
volatile unsigned int
CMEOR
;
49
}
CCM_Type
;
50
51
typedef
struct
52
{
53
volatile unsigned int
PLL_ARM
;
54
volatile unsigned int
PLL_ARM_SET
;
55
volatile unsigned int
PLL_ARM_CLR
;
56
volatile unsigned int
PLL_ARM_TOG
;
……
110
volatile unsigned int
MISC2
;
111
volatile unsigned int
MISC2_SET
;
112
volatile unsigned int
MISC2_CLR
;
113
volatile unsigned int
MISC2_TOG
;
114
}
CCM_ANALOG_Type
;
115
116
/*
117
* IOMUX
寄存器组
118
*/
119
typedef
struct
120
{
121
volatile unsigned int
BOOT_MODE0
;
122
volatile unsigned int
BOOT_MODE1
;
123
volatile unsigned int
SNVS_TAMPER0
;
……
241
volatile unsigned int
CSI_DATA04
;
242
volatile unsigned int
CSI_DATA05
;
243
volatile unsigned int
CSI_DATA06
;
244
volatile unsigned int
CSI_DATA07
;
245
}
IOMUX_SW_MUX_Type
;
246
247
typedef
struct
248
{
249
volatile unsigned int
DRAM_ADDR00
;
250
volatile unsigned int
DRAM_ADDR01
;
……
419
volatile unsigned int
GRP_DDRPKE
;
420
volatile unsigned int
GRP_DDRMODE
;
421
volatile unsigned int
GRP_DDR_TYPE
;
422
}
IOMUX_SW_PAD_Type
;
423
424
/*
425
* GPIO
寄存器结构体
426
*/
427
typedef
struct
428
{
429
volatile unsigned int
DR
;
430
volatile unsigned int
GDIR
;
431
volatile unsigned int
PSR
;
432
volatile unsigned int
ICR1
;
433
volatile unsigned int
ICR2
;
434
volatile unsigned int
IMR
;
435
volatile unsigned int
ISR
;
436
volatile unsigned int
EDGE_SEL
;
437
}
GPIO_Type
;
438
439
440
/*
441
*
外设指针
442
*/
443
#define CCM
((
CCM_Type
*)
CCM_BASE
)
444
#define CCM_ANALOG
((
CCM_ANALOG_Type
*)
CCM_ANALOG_BASE
)
445
#define IOMUX_SW_MUX
((
IOMUX_SW_MUX_Type
*)
IOMUX_SW_MUX_BASE
)
446
#define IOMUX_SW_PAD
((
IOMUX_SW_PAD_Type
*)
IOMUX_SW_PAD_BASE
)
447
#define GPIO1
((
GPIO_Type
*)
GPIO1_BASE
)
448
#define GPIO2
((
GPIO_Type
*)
GPIO2_BASE
)
449
#define GPIO3
((
GPIO_Type
*)
GPIO3_BASE
)
450
#define GPIO4
((
GPIO_Type
*)
GPIO4_BASE
)
451
#define GPIO5
((
GPIO_Type
*)
GPIO5_BASE
)
在编写寄存器组结构体的时候注意寄存器的地址是否连续,有些外设的寄存器地址可能不是连续的,会有一些保留地址,因此我们需要在结构体中留出这些保留的寄存器。比如 CCM
的CCGR6 寄存器地址为
0X020C4080
,而寄存器
CMEOR
的地址为
0X020C4088
。按照地址顺序递增的原理,寄存器 CMEOR
的地址应该是
0X020C4084
,但是实际上
CMEOR
的地址是 0X020C4088,相当于中间跳过了
0X020C4088-0X020C4080=8
个字节,如果寄存器地址连续的 话应该只差 4
个字节
(32
位
)
,但是现在差了
8
个字节,所以需要在寄存器
CCGR6
和
CMEOR直接加入一个保留寄存器,这个就是“示例代码 11.3.1
”中第
47
行
RESERVED_3[1]
的来源。如果不添加保留位来占位的话就会导致寄存器地址错位!main.c 文件中输入如下所示内容:
1
#include
"imx6ul.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
复用
*/
27
IOMUX_SW_MUX
->
GPIO1_IO03
=
0X5
;
/*
复用为
GPIO1_IO03 */
28
29
30
/* 2
、配置
GPIO1_IO03
的
IO
属性
31
*bit 16:0 HYS
关闭
32
*bit [15:14]: 00
默认下拉
33
*bit [13]: 0 kepper
功能
34
*bit [12]: 1 pull/keeper
使能
35
*bit [11]: 0
关闭开路输出
36
*bit [7:6]: 10
速度
100Mhz
37
*bit [5:3]: 110 R0/6
驱动能力
38
*bit [0]: 0
低转换率
39
*/
40
IOMUX_SW_PAD
->
GPIO1_IO03
=
0X10B0
;
41
42
43
/* 3
、初始化
GPIO */
44
GPIO1
->
GDIR
=
0X0000008
;
/* GPIO1_IO03
设置为输出
*/
45
46
/* 4
、设置
GPIO1_IO03
输出低电平,打开
LED0 */
47
GPIO1
->
DR
&= ~(
1
<<
3
);
48
49
}
50
51
/*
52
* @description :
打开
LED
灯
53
* @param :
无
54
* @return :
无
55
*/
56
void
led_on
(
void
)
57
{
58
/*
将
GPIO1_DR
的
bit3
清零
*/
59
GPIO1
->
DR
&= ~(
1
<<
3
);
60
}
61
62
/*
63
* @description :
关闭
LED
灯
64
* @param :
无
65
* @return :
无
66
*/
67
void
led_off
(
void
)
68
{
69
/*
将
GPIO1_DR
的
bit3
置
1 */
70
GPIO1
->
DR
|= (
1
<<
3
);
71
}
72
73
/*
74
* @description :
短时间延时函数
75
* @param - n :
要延时循环次数
(
空操作循环次数,模式延时
)
76
* @return :
无
77
*/
78
void
delay_short
(
volatile unsigned int
n
)
79
{
80
while
(
n
--){}
81
}
82
83
/*
84
* @description :
延时函数
,
在
396Mhz
的主频下
85
*
延时时间大约为
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 : main
函数
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
个函数的含义和第十章中的
main.c
文件一样,只是函数体写法变了,寄存器的访问采用 imx6ul.h
中定义的外设指针。比如第
27
行设置
GPIO1_IO03
的复用功能就可以通过“IOMUX_SW_MUX->GPIO1_IO03
”来给寄存
SW_MUX_CTL_PAD_GPIO1_IO03赋值。
4.4.1 编写 Makefile 和链接脚本
Makefile
文件的内容基本和第十章的
Makefile
一样,如下:
1
objs
:=
start.o main.o
2
3
ledc.bin
:
$(objs)
4
arm-linux-gnueabihf-ld -Timx6ul.lds -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
链接脚本
imx6ul.lds
的内容和上一章一样,可以直接使用上一章的链接脚本文件。