实验:模仿STM32驱动开发格式
1.模仿 STM32 寄存器定义
为了开发方便,
ST
官方为
STM32F103
编写了一个叫做
stm32f10x.h
的文件,在这个文件里面定义了
STM32F103 所有外设寄存器,我们可以使用其定义的寄存器来进行开发,比如我 们可以用如下代码来初始化一个 GPIO:
![]()
上述代码是初始化
STM32
的
PE5
这个
GPIO
为推挽输出,需要配置的就是
GPIOE
的寄存器
CRL
和
ODR
, “GPIOE”的定义:
![]()
可以看出“
GPIOE
”是个宏定义,是一个指向地址
GPIOE_BASE
的结构体指针,结构体为GPIO_TypeDef
,
GPIO_TypeDef
和 GPIOE_BASE 的定义如下:
![]()
上述定义中
GPIO_TypeDef
是个结构体,结构体里面的成员变量有
CRL
、
CRH
、IDR
、
ODR
、 BSRR、
BRR
和
LCKR
,这些都是
GPIO
的寄存器,每个成员变量都是
32
位
(4
字节
)
,这些寄存 器在结构体中的位置都是按照其地址值从小到大排序的。GPIOE_BASE
就是 GPIOE 的基地址, 其为:
![]()
GPIOE_BASE
的基地址为
0x40011800
,宏
GPIOE
指向这个地址,因此
GPIOE
的寄存器CRL
的地址就是
0X40011800
,寄存器
CRH
的地址就是
0X40011800+4=0X40011804
,其他寄存 器地址以此类推。我们要操作 GPIOE
的
ODR
寄存器的话就可以通过“
GPIOE->ODR
”来实现, 这个方法是借助了结构体成员地址连续递增的原理。
2.I.MX6U 寄存器定义
1、编写外设结构体
先将同属于一个外设的所有寄存器编写到一个结构体里面,如
IO
复用寄存器组的结构体如下:
//示例代码 11.1.2.1 寄存器 IOMUX_SW_MUX_Type /* * IOMUX 寄存器组 */ 1 typedef struct 2 { 3 volatile unsigned int BOOT_MODE0; 4 volatile unsigned int BOOT_MODE1; 5 volatile unsigned int SNVS_TAMPER0; 6 volatile unsigned int SNVS_TAMPER1; ……… 107 volatile unsigned int CSI_DATA00; 108 volatile unsigned int CSI_DATA01; 109 volatile unsigned int CSI_DATA02; 110 volatile unsigned int CSI_DATA03; 111 volatile unsigned int CSI_DATA04; 112 volatile unsigned int CSI_DATA05; 113 volatile unsigned int CSI_DATA06; 114 volatile unsigned int CSI_DATA07; /* 为了缩短代码,其余 IO 复用寄存器省略 */ 115}IOMUX_SW_MUX_Tpye;
上述结构体
IOMUX_SW_MUX_Type
就是
IO
复用寄存器组,成员变量是每个
IO
对应的复用寄存器,每个寄存器的地址是
32
位,每个成员都使用“
volatile
”进行了修饰,目的是防止编译器优化。
2、定义 IO 复用寄存器组的基地址
根据结构体
IOMUX_SW_MUX_Type
的定义,其第一个成员变量为
BOOT_MODE0
,也就是
BOOT_MODE0
这个
IO
的
IO
复用寄存器,查找
I.MX6U
的参考手册可以得知其地址为0X020E0014
,所以
IO
复用寄存器组的基地址就是 0X020E0014,定义如下:
![]()
3、定义访问指针
访问指针定义如下:
![]()
通过上面三步我们就可以通过“
IOMUX_SW_MUX->GPIO1_IO03
”来访问
GPIO1_IO03
的IO
复用寄存器
4.实验程序编写
本实验对应的例程路径为:
开发板光盘
-> 1
、裸机例程
-> 3_ledc_stm32
。创建
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)
在编写寄存器组结构体的时候注意寄存器的地址是否连续,有些外设的寄存器地址可能不
是连续的,会有一些保留地址,因此我们需要在结构体中留出这些保留的寄存器。这个就是
第
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 赋值。
编译下载验证
编写
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
编译下载
使用
Make
命令编译代码,编译成功以后使用软件
imxdownload
将编译完成的
ledc.bin
文件下载到 SD 卡中,命令如下:
![]()
烧写成功以后将
SD
卡插到开发板的
SD
卡槽中,然后复位开发板,如果代码运行正常的话
LED0
就会以
500ms
的时间间隔亮灭