沙丁鱼介绍

首页 » 常识 » 诊断 » 正点原子Linux连载第十一章模仿S
TUhjnbcbe - 2023/7/9 21:19:00
现在治疗白癜风要多少钱 https://m-mip.39.net/baidianfeng/mipso_4513569.html

第十一章模仿STM32驱动开发格式实验

在上一章使用C语言编写LED灯驱动的时候,每个寄存器的地址我们都需要写宏定义,使用起来非常的不方便。我们在学习STM32的时候,可以使用“GPIOB-ODR”这种方式来给GPIOB的寄存器ODR赋值,因为在STM32中同属于一个外设的所有寄存器地址基本是相邻的(有些会有保留寄存器)。因此我们可以借助C语言里面的结构体成员地址递增的特点来将某个外设的所有寄存器写入到一个结构体里面,然后定义一个结构体指针指向这个外设的寄存器基地址,这样我们就可以通过这个结构体指针来访问这个外设的所有寄存器。同理,I.MX6U也可以使用这种方法来定义外设寄存器,本章我们就模仿STM32里面的寄存器定义方式来编写I.MX6U的驱动,通过本章的学习也可以对STM32的寄存器定义方式有一个深入的认识。

11.1模仿STM32寄存器定义

11.1.1STM32寄存器定义简介

为了开发方便,ST官方为STM32F编写了一个叫做stm32f10x.h的文件,在这个文件里面定义了STM32F所有外设寄存器,我们可以使用其定义的寄存器来进行开发,比如我们可以用如下代码来初始化一个GPIO:

GPIOE-CRL=0XFF0FFFFF;

GPIOE-CRL

=0X;    //PE5推挽输出

GPIOE-ODR

=15;    //PE5输出高

上述代码是初始化STM32的PE5这个GPIO为推挽输出,需要配置的就是GPIOE的寄存器CRL和ODR,“GPIOE”的定义:

#defineGPIOE((GPIO_TypeDef*)GPIOE_BASE)

可以看出“GPIOE”是个宏定义,是一个指向地址GPIOE_BASE的结构体指针,结构体为GPIO_TypeDef,GPIO_TypeDef和GPIOE_BASE的定义如下:

typedefstruct

{

__IOuint32_tCRL;

__IOuint32_tCRH;

__IOuint32_tIDR;

__IOuint32_tODR;

__IOuint32_tBSRR;

__IOuint32_tBRR;

__IOuint32_tLCKR;

}GPIO_TypeDef;

#defineGPIOE_BASE  (APB2PERIPH_BASE+0x)

#defineAPB2PERIPH_BASE  (PERIPH_BASE+0x)

#definePERIPH_BASE  ((uint32_t)0x)

上述定义中GPIO_TypeDef是个结构体,结构体里面的成员变量有CRL、CRH、IDR、ODR、BSRR、BRR和LCKR,这些都是GPIO的寄存器,每个成员变量都是32位(4字节),这些寄存器在结构体中的位置都是按照其地址值从小到大排序的。GPIOE_BASE就是GPIOE的基地址,其为:

GPIOE_BASE=APB2PERIPH_BASE+0x

=PERIPH_BASE+0x+0x

=0x+0x+0x

=0x4001

GPIOE_BASE的基地址为0x4001,宏GPIOE指向这个地址,因此GPIOE的寄存器CRL的地址就是0X4001,寄存器CRH的地址就是0X4001+4=0X,其他寄存器地址以此类推。我们要操作GPIOE的ODR寄存器的话就可以通过“GPIOE-ODR”来实现,这个方法是借助了结构体成员地址连续递增的原理。

了解了STM32的寄存器定义以后,我们就可以参考其原理来编写I.MX6U的外设寄存器定义了。NXP官方并没有为I.MX6UL编写类似stm32f10x.h这样的文件,NXP只为I.MX6ULL提供了类似stm32f10x.h这样的文件,名为MCIMX6Y2.h,但是I.MX6UL和I.MX6ULL几乎一模一样,所以文件MCIMX6Y2.h可以用在I.MX6UL上。关于文件MCIMX6Y2.h的移植我们在下一章讲解,本章我们参考stm32f10x.h来编写一个简单的MCIMX6Y2.h文件。

11.1.2I.MX6U寄存器定义

参考STM32的官方文件来编写I.MX6U的寄存器定义,比如IO复用寄存器组“IOMUX_SW_MUX_CTL_PAD_XX”,步骤如下:

1、编写外设结构体

先将同属于一个外设的所有寄存器编写到一个结构体里面,如IO复用寄存器组的结构体如下:

示例代码11.1.2.1寄存器IOMUX_SW_MUX_Type

/*

*IOMUX寄存器组

*/

1typedefstruct

2{

3  volatileunsignedintBOOT_MODE0;

4  volatileunsignedintBOOT_MODE1;

5  volatileunsignedintSNVS_TAMPER0;

6  volatileunsignedintSNVS_TAMPER1;

………

  volatileunsignedintCSI_DATA00;

  volatileunsignedintCSI_DATA01;

  volatileunsignedintCSI_DATA02;

  volatileunsignedintCSI_DATA03;

  volatileunsignedintCSI_DATA04;

  volatileunsignedintCSI_DATA05;

  volatileunsignedintCSI_DATA06;

  volatileunsignedintCSI_DATA07;

/*为了缩短代码,其余IO复用寄存器省略*/

}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的参考手册可以得知其地址为0XE,所以IO复用寄存器组的基地址就是0XE,定义如下:

#defineIOMUX_SW_MUX_BASE      (0XE)

3、定义访问指针

访问指针定义如下:

#defineIOMUX_SW_MUX    ((IOMUX_SW_MUX_Type*)IOMUX_SW_MUX_BASE)

通过上面三步我们就可以通过“IOMUX_SW_MUX-GPIO1_IO03”来访问GPIO1_IO03的IO复用寄存器了。同样的,其他的外设寄存器都可以通过这三步来定义。

11.2硬件原理分析

本章使用到的硬件资源和第八章一样,就是一个LED0。

11.3实验程序编写

本实验对应的例程路径为:开发板光盘-1、裸机例程-3_ledc_stm32。

创建VSCode工程,工作区名字为“ledc_stm32”,新建三个文件:start.S、main.c和imx6ul.h。其中start.S是汇编文件,start.S文件的内容和第十章的start.S一样,直接复制过来就可以。main.c和imx6ul.h是C文件,完成以后如图11.3.1所示:

图11.3.1工程文件目录

文件imx6ul.h用来存放外设寄存器定义,在imx6ul.h中输入如下代码:

示例代码11.2.1imx6ul.h文件代码

/***************************************************************

CopyrightzuozhongkaiCo.,Ltd.-.Allrightsreserved.

文件名:imx6ul.h

作者:左忠凯

版本:V1.0

描述:IMX6UL相关寄存器定义,参考STM32寄存器定义方法

其他:无

日志:初版V1.19/1/3左忠凯创建

**************************************************************/

/*

*外设寄存器组的基地址

*/

1#defineCCM_BASE    (0XC)

2#defineCCM_ANALOG_BASE  (0XC)

3#defineIOMUX_SW_MUX_BASE  (0XE)

4#defineIOMUX_SW_PAD_BASE  (0XE4)

5#defineGPIO1_BASE  (0x9C)

6#defineGPIO2_BASE  (0xA0)

7#defineGPIO3_BASE  (0xA)

8#defineGPIO4_BASE  (0xA)

9#defineGPIO5_BASE  (0xAC)

10

11/*

12*CCM寄存器结构体定义,分为CCM和CCM_ANALOG

13*/

14typedefstruct

15{

16volatileunsignedintCCR;

17volatileunsignedintCCDR;

18volatileunsignedintCSR;

……

46volatileunsignedintCCGR6;

47volatileunsignedintRESERVED_3[1];

48volatileunsignedintCMEOR;

49}CCM_Type;

50

51typedefstruct

52{

53volatileunsignedintPLL_ARM;

54volatileunsignedintPLL_ARM_SET;

55volatileunsignedintPLL_ARM_CLR;

56volatileunsignedintPLL_ARM_TOG;

……

volatileunsignedintMISC2;

volatileunsignedintMISC2_SET;

volatileunsignedintMISC2_CLR;

volatileunsignedintMISC2_TOG;

}CCM_ANALOG_Type;

/*

*IOMUX寄存器组

*/

typedefstruct

{

volatileunsignedintBOOT_MODE0;

volatileunsignedintBOOT_MODE1;

volatileunsignedintSNVS_TAMPER0;

……

volatileunsignedintCSI_DATA04;

volatileunsignedintCSI_DATA05;

volatileunsignedintCSI_DATA06;

volatileunsignedintCSI_DATA07;

}IOMUX_SW_MUX_Type;

typedefstruct

{

volatileunsignedintDRAM_ADDR00;

volatileunsignedintDRAM_ADDR01;

……

volatileunsignedintGRP_DDRPKE;

volatileunsignedintGRP_DDRMODE;

volatileunsignedintGRP_DDR_TYPE;

}IOMUX_SW_PAD_Type;

/*

*GPIO寄存器结构体

*/

typedefstruct

{

volatileunsignedintDR;

volatileunsignedintGDIR;

volatileunsignedintPSR;

volatileunsignedintICR1;

volatileunsignedintICR2;

volatileunsignedintIMR;

volatileunsignedintISR;

volatileunsignedintEDGE_SEL;

}GPIO_Type;

/*

*外设指针

*/

#defineCCM  ((CCM_Type*)CCM_BASE)

#defineCCM_ANALOG  ((CCM_ANALOG_Type*)CCM_ANALOG_BASE)

#defineIOMUX_SW_MUX  ((IOMUX_SW_MUX_Type*)IOMUX_SW_MUX_BASE)

#defineIOMUX_SW_PAD  ((IOMUX_SW_PAD_Type*)IOMUX_SW_PAD_BASE)

#defineGPIO1  ((GPIO_Type*)GPIO1_BASE)

#defineGPIO2  ((GPIO_Type*)GPIO2_BASE)

#defineGPIO3  ((GPIO_Type*)GPIO3_BASE)

#defineGPIO4  ((GPIO_Type*)GPIO4_BASE)

#defineGPIO5  ((GPIO_Type*)GPIO5_BASE)

在编写寄存器组结构体的时候注意寄存器的地址是否连续,有些外设的寄存器地址可能不是连续的,会有一些保留地址,因此我们需要在结构体中留出这些保留的寄存器。比如CCM的CCGR6寄存器地址为0XC,而寄存器CMEOR的地址为0XC。按照地址顺序递增的原理,寄存器CMEOR的地址应该是0XC,但是实际上CMEOR的地址是0XC,相当于中间跳过了0XC-0XC=8个字节,如果寄存器地址连续的话应该只差4个字节(32位),但是现在差了8个字节,所以需要在寄存器CCGR6和CMEOR直接加入一个保留寄存器,这个就是“示例代码11.3.1”中第47行RESERVED_3[1]的来源。如果不添加保留为来占位的话就会导致寄存器地址错位!

main.c文件中输入如下所示内容:

示例代码11.3.2main.c文件代码

1#includeimx6ul.h

2

3/*

4*

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

5*

param:无

6*

return:无

7*/

8voidclk_enable(void)

9{

10CCM-CCGR0=0XFFFFFFFF;

11CCM-CCGR1=0XFFFFFFFF;

12CCM-CCGR2=0XFFFFFFFF;

13CCM-CCGR3=0XFFFFFFFF;

14CCM-CCGR4=0XFFFFFFFF;

15CCM-CCGR5=0XFFFFFFFF;

16CCM-CCGR6=0XFFFFFFFF;

17}

18

19/*

20*

description:初始化LED对应的GPIO

21*

param:无

22*

return:无

23*/

24voidled_init(void)

25{

26/*1、初始化IO复用*/

27IOMUX_SW_MUX-GPIO1_IO03=0X5;/*复用为GPIO1_IO03*/

28

29

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

31*bit16:0HYS关闭

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

33*bit[13]:0kepper功能

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

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

36*bit[7:6]:10速度Mhz

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

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

39*/

40IOMUX_SW_PAD-GPIO1_IO03=0X10B0;

41

42

43/*3、初始化GPIO*/

44GPIO1-GDIR=0X8;/*GPIO1_IO03设置为输出*/

45

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

47GPIO1-DR=~(13);

48

49}

50

51/*

52*

description:打开LED灯

53*

param:无

54*

return:无

55*/

56voidled_on(void)

57{

58/*将GPIO1_DR的bit3清零*/

59GPIO1-DR=~(13);

60}

61

62/*

63*

description:关闭LED灯

64*

param:无

65*

return:无

66*/

67voidled_off(void)

68{

69/*将GPIO1_DR的bit3置1*/

70GPIO1-DR

=(13);

71}

72

73/*

74*

description:短时间延时函数

75*

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

76*

return:无

77*/

78voiddelay_short(volatileunsignedintn)

79{

80while(n--){}

81}

82

83/*

84*

description:延时函数,在Mhz的主频下

85*延时时间大约为1ms

86*

param-n:要延时的ms数

87*

return:无

88*/

89voiddelay(volatileunsignedintn)

90{

91while(n--)

92{

93delay_short(0x7ff);

94}

95}

96

97/*

98*

description:mian函数

99*

param:无

*

return:无

*/

intmain(void)

{

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

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

while(1)/*死循环  */

{

led_off();/*关闭LED  */

delay();/*延时ms  */

led_on();/*打开LED  */

delay();/*延时ms  */

}

return0;

}

main.c中7个函数,这7个函数的含义和第十章中的main.c文件一样,只是函数体写法变了,寄存器的访问采用imx6ul.h中定义的外设指针。比如第27行设置GPIO1_IO03的复用功能就可以通过“IOMUX_SW_MUX-GPIO1_IO03”来给寄存SW_MUX_CTL_PAD_GPIO1_IO03赋值。

11.4编译下载验证

11.4.1编写Makefile和链接脚本

Makefile文件的内容基本和第十章的Makefile一样,如下:

示例代码11.4.1Makefile文件代码

1objs:=start.omain.o

2

3ledc.bin:$(objs)

4    arm-linux-gnueabihf-ld-Timx6ul.lds-oledc.elf$^

5  arm-linux-gnueabihf-objcopy-Obinary-Sledc.elf$

6  arm-linux-gnueabihf-objdump-D-marmledc.elfledc.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

17clean:

18  rm-rf*.oledc.binledc.elfledc.dis

链接脚本imx6ul.lds的内容和上一章一样,可以直接使用上一章的链接脚本文件。

11.4.2编译下载

使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的ledc.bin文件下载到SD卡中,命令如下:

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

./imxdownloadledc.bin/dev/sdd    //烧写到SD卡中

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

1
查看完整版本: 正点原子Linux连载第十一章模仿S