读者需要注意的是,存储器本身不具有地址信息,他的地址是芯片厂或用户分配的,给存储器分配地址的过程称之为存储器映射,如果给存储器再分配一个地址,这个过程称为存储器重映射,说白了就是重复映射的过程。 由上表可知,片上系统有两大类总线,由于外设执行速度不同,因此挂载在不同的外设上,APB挂载低速外设(例如GPIO、UART、SPI、IIC等),AHB挂载高速外设(例如DMA、SDIO、NORFlash、SDRAM、LCD等)。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中APB1总线的地址最低,片上外设从这里开始,也叫外设基地址。 4.2.2SWM320的寄存器映射接下来我们再来看看寄存器的映射关系,SWM320这样的控制器,有成百上千的寄存器,我们不可能一一讲述,这里随便列举一个系统管理所用到的部分寄存器,其对应关系如表4-2所示,光这个基地址(由表4-1可知地址为:0x40000000)上就分配这好多寄存器,这里列举部分,以做讲解。 表4-2 SYSCON寄存器映射表 名称 | 偏移量 | 类型 | 复位值 | 描述 | CLKSEL | 0x00 | R/W | 0 | 时钟选择控制寄存器 | CLKDIV | 0x04 | R/W | 0 | 时钟分频寄存器 | CLKEN | 0x08 | R/W | 0 | 时钟门控寄存器 | … | … | … | … | … |
走到这里,需要给读者再讲讲故事。上面说到,存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?在存储器这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。 学到这里,如果读者要访问CLKEN寄存器,请问访问地址是多少?带着这个问题,我们继续往下学习。 接下来,我们再看看CLKEN寄存器究竟控制那些东西?先看寄存器CLKEN的列表,如表4-3所示。同样,该寄存器有32位,我们不可能一一讲述,我们先列举几个,讲解即可。由上可知,CLKEN的寄存器映射地址为0x40000008,因为SYSCON的基地址是0x40000000,而CLKEN的偏移地址是0x08,两者相加即为0x40000008。 表4-3 CLKEN寄存器列表 位域 | 名称 | 类型 | 复位值 | 描述 | 31 | REVERSED | R | 0 | 保留位 | 30 | ADC1 | R/W | 0 | ADC1时钟使能 1:使能 0:不使能 | 29 | SDIO | R/W | 0 | SDIO时钟使能 1:使能 0:不使能 | 28 | RAMC | R/W | 0 | RAMC时钟使能 1:使能 0:不使能 | 27 | NORFL | R/W | 0 | NORFL时钟使能 1:使能 0:不使能 | 26 | SDRAM | R/W | 0 | SDRAM时钟使能 1:使能 0:不使能 | 25 | CAN | R/W | 0 | CAN时钟使能 1:使能 0:不使能 | 24 | RTCBKP | R/W | 0 | RTCBKP时钟使能 1:使能 0:不使能 | 23 | CRC | R/W | 0 | CRC时钟使能 1:使能 0:不使能 | 22 | ANAC | R/W | 0 | ANAC时钟使能 1:使能 0:不使能 | 21 | GPIOP | R/W | 0 | GPIOP时钟使能 1:使能 0:不使能 | … | … | … | … | … |
由此可见,这个寄存器是用来控制外设时钟的,因为SWM320为了降低功耗,每个外设的时钟是可控的,默认情况下,都是关闭的,要使用某个外设,必须先使能外设的时钟,只有这样,外设才可用。FSSW32核心板上的用户LED接在GPIOP.22口上,由此可见,要操作LED必须先使能GPIOP口的时钟,那如何使能?由表4-3可知,只需给CLKEN寄存器的第21位赋值1则可以使能,那如何给这个位赋值1?执行语句如下。 *(volatile unsigned long *)0x40000008 |= 1 << 21; 看到这里,请问读者能否彻底理解这句源码的意思?如果能熟练理解,说明C语言的基本功还是有的,如果读者不能理解,请马上先去补补C语言基础,特别是指针章节。既然讲解到这里,就为读者解释一下这句话的含义。 其中volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到;unsigned long意思是无符号的长整型变量。这样我们可以将(volatile unsigned long *)0x40000008理解为:将其地址0x40000008强制转换成一个指针,并且该指针指向一个易变的无符号整数。那么“*(volatile unsigned long *)0x40000008”就是对这个内存地址进行操作(如读写操作)。“|”和“<<”是C语言最基本的操作符,如果连这个都不懂,还是赶紧放弃单片机的学习算了,哈哈。整个语句的意思就是:给CLKEN寄存器的第21位赋值“1”,也即使能GPIOP口的时钟。 4.3用寄存操作的方式点亮FSSW32的LED接下来我们重新建立工程,不用官方的库函数,用寄存器来点亮LED,建立好的工程如图4-1所示,工程建立参见第3章,读者需要注意的是,这里只需加入一个启动程序,也即“startup_SWM320.s”,剩下的一概不需要,连头文件都不需要。
图4-1 寄存器版的LED功能实例 如图4-1所示的第4行程序我们已经上面分析讲述了,接下来我们分析一下第6行。由表4-1可知,GPIOP口的基地址为0x40018000;由SWM320的规格书第6.7.3节可知,DIR(GPIO方向寄存器)的偏移量为0x04,可知DIR寄存器的地址就为:0x40018000+0x04 = 0x40018004,上面有说,我们的用户LED接在GPIOP.22口上,因此这里需要将GPIOP口DIR寄存器的第22位置“1”,这样该端口就为输出端口了。按道理,这两步设置好之后,应该给对应的端口赋值0,才为低电平点亮,可由规格书可知,数据寄存器的默认值为0,因此我们省略了这一步。到此,编译,下载到开发板上,LED小灯就会点亮。
|