EasyARM1138学习过程Word格式.docx
- 文档编号:21632298
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:28
- 大小:1.44MB
EasyARM1138学习过程Word格式.docx
《EasyARM1138学习过程Word格式.docx》由会员分享,可在线阅读,更多相关《EasyARM1138学习过程Word格式.docx(28页珍藏版)》请在冰豆网上搜索。
具体是这样的:
Stellaris器件内部的SRAM的地址是0x2000.0000,为了减少读-修改-写(RMW)
操作的时间,ARM在Cortex-M3处理器中引入了bit-banding技术。
在bit-banding使能的处理器中,
存储器映射的特定区域(SRAM和外设空间)能够使用地址别名,在单个原子操作中访问各个位。
使用下面的公式来计算bit-band别名:
bit-band别名=bit-band基址+(字节偏移量*32)+(位编号*4)
例如,如果要修改地址0x2000.1000的位3,则bit-band别名计算如下:
0x2200.0000+(0x1000*32)+(3*4)=0x2202.000C
通过计算得出的别名地址,对地址0x2202.000C执行读/写操作的指令仅允许直接访问地址0x2000.1000处字节的位3。
其中0x2200.0000是bit-band基址的起始地址,0x1000是偏移地址,3是位编号。
好,这里先告一段落,我们继续看前面的宏:
#defineHWREG(x)
(*
(
(volatile
unsigned
long*
)
(x)
)
这一句后面的(*((volatileunsignedlong*)(x)))
是什么意思呢?
我也不知道了,那么XX一下吧。
搜到这样的文章:
#defineA(*(volatileunsignedlong*)详解
2008/08/0309:
00A.M.
#defineA
(*(volatileunsignedlong*)
0x48000000)
对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。
如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。
如果是内存映射,那就方便的多了。
举个例子,比如像寄存器A(地址假定为0x48000000)写入数据0x01,那么就可以这样设置了。
#defineA(*(volatileunsignedlong*)0x48000000)
...
A=0x01;
这实际上就是内存映射机制的方便性了。
其中volatile关键字是嵌入式系统开发的一个重要特点。
volatile
(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。
这
种“意想不到地改变”,不是由程序去改变,而是由硬件去改变。
volatile限定编译器不对这个指针的指向的存储单元进行优化,即不用通用寄存器暂时代替这个指针的指向
的存储单元,而是每次取值都直接到指针的指向的存储单元取值.volatile主要用于变量会异步改变的情况下,
主要有三个方面:
1.cpu外设寄存器2.中断和主循环都会用到的全局变量
3.操作系统中的线程间都会用到
的公共变量.上述表达式拆开来分析,
首先(volatileunsignedlong*)0x48000000的意思是把0x48000000强制转换成volatileunsignedlong类型
的指针,即对指针的操作的范围是从0x48000000开始的4个字节(long型).暂记为p,那么就是
*p,即A为P指针指向位置的内容了。
这里就是通过内存寻址访问到寄存器A,可以读/写操作!
仔细看看这位兄台的文章,说的很清楚了。
那么我们的那句:
就是等于#define
HWREG(x)
(x),这里的(x)就是你想设置的存储器地址了。
例如HWREG(0x22000000)=0x12就是把0x12这个数存储在地址0x22000000里面。
呵呵~
A="
HWREG"
(0x22000000)就是把存储空间0x22000000里面的数据读出来。
OK,这里搞定!
接下来看下面这个宏:
#defineHWREGBITW(x,b)
HWREG(((unsignedlong)(x)&
0xF0000000)|0x02000000|
(((unsignedlong)(x)&
0x000FFFFF)<
<
5)|((b)<
2))
初一看有点长,其实不用怕。
一步一步来:
首先根据C语言的语法规则()的优先级最高,那先算()里面的呗。
b<
2=b*4记为b1;
(x)&
0xF000000就得到高4位的值,低28位置0记为X1;
x&
0x000FFFFFF就得到低20位的值,高12位置0记为X2;
X2<
5=X2*32记为X3;
X1|0x02000000记为X4;
X3|X4记为X5;
好,现在设x=0x20001000,b=3。
那么算得X5=0x2202000C。
这个X5貌似见过,对,哈哈就是上文的bit-band的值。
现在清楚了:
这个宏的作用就是计算对应的bit-band的地址。
例如:
HWREGBITW(0x20001000,3)=1就表示把1写进0x20001000存储空间的位3,而且对应的bit-band地址是0x2202000C的位3。
同理下面两个defineHWREGBITH(x,b)
HWREGH(((unsignedlong)(x)&
#defineHWREGBITB(x,b)
HWREGB(((unsignedlong)(x)&
也是一样的,只是数据的宽度不同。
至此,hw_types.h头文件里面的宏都差不多弄清楚了。
说差不多是因为还有些我不懂的东西,贴图期待高手解决:
今天是学习LM3s1138的第二天,我们接着昨天的继续学习。
昨天我打开了SystemInit.h的头文件,深入学习了里面的hw_types.h子头文件。
今天我开始学习另外的几个头文件:
hw_memmap.h
。
这个头文件就比较简单了从字面意思看就知道是存储器映射的头文件。
我们打开看看,果不其然:
这和LM3s1138datasheet上是一样的:
只是这个头文件里面只定义了它的起始地址。
还有些存储器的映射我这里就不一一例举了。
好,接下来看看hw_ints.h里面的东西:
从第一行看这是一个定义错误的任务;
我们看看datasheet上是怎么说的:
我的理解是这样的:
CPU的中断是由异常引发的,异常也有多种。
于是怎么处理多个异常呢?
ARMCortex-M3处理器和嵌套向量中断控制器(NVIC)区分所有的异常并对其进行处理。
在出现异常时,处理器的状态被自动存储在堆栈中,并在中断服务程序结束后自动从堆栈中恢复。
所以CPU先给每个异常分配一个堆栈地址即向量号。
这样就处理好了异常的先后顺序。
比如:
#define
FAULT_NMI
2
就表示不可屏蔽的中断(NMI)是优先级为2的级别
这里就用FAULT_NMI
表示它在堆栈中的具体优先级2,这样既清楚又方便。
其他的同理。
继续往下看:
这定义的是中断顺序号和上面的异常是一样的,这里就不在啰唆了。
好,那么关于hw_ints.h头文件就初步学习了。
下午再看下面的<
interrupt.h>
:
//*****************************************************************************
//
//interrupt.h-PrototypesfortheNVICInterruptControllerDriver.
//Copyright(c)2005-2008LuminaryMicro,Inc.
Allrightsreserved.
//
//SoftwareLicenseAgreement
#ifndef__INTERRUPT_H__
#define__INTERRUPT_H__
//IfbuildingwithaC++compiler,makeallofthedefinitionsinthisheader
//haveaCbinding.
#ifdef__cplusplus
extern"
C"
{
#endif
//Macrotogenerateaninterruptprioritymaskbasedonthenumberofbits
//ofprioritysupportedbythehardware.
#defineINT_PRIORITY_MASK
((0xFF<
(8-NUM_PRIORITY_BITS))&
0xFF)
//PrototypesfortheAPIs.
externtBooleanIntMasterEnable(void);
externtBooleanIntMasterDisable(void);
externvoidIntRegister(unsignedlongulInterrupt,void(*pfnHandler)(void));
externvoidIntUnregister(unsignedlongulInterrupt);
externvoidIntPriorityGroupingSet(unsignedlongulBits);
externunsignedlongIntPriorityGroupingGet(void);
externvoidIntPrioritySet(unsignedlongulInterrupt,
unsignedcharucPriority);
externlongIntPriorityGet(unsignedlongulInterrupt);
externvoidIntEnable(unsignedlongulInterrupt);
externvoidIntDisable(unsignedlongulInterrupt);
//MarktheendoftheCbindingssectionforC++compilers.
}
看起来有点复杂,但是不要怕相信毛主席那句“一切反动派都是纸老虎”。
一步一步来看。
先看看说明文档。
英文意思是说intrrupt.h头文件是NVIC中断控制器的驱动器原型。
再下面的英文意思是如果是一个C++编译器,要把#ifdef__cplusplus
#endif
包含进去。
这里声明了一个外部变量“C”,只要__cplusplus在前面被定义过,那么就可以使用
“C”这个字符了。
这种条件编译对于提高C源程序的通用性是很有好处的。
如果一个C源程序在不同计算机系统上运行,而不同的计算机又有一定的差异(例如,有的机器以16位存放一个整数,有的是32位存放一个整数),这样往往需要对源程序做必要的修改,这就降低了程序的通用性。
再回到这里,那如果没有申明__cplusplus呢?
查查《C语言程序设计》的条件编译那节说得很详细。
就执行下面的程序。
这里我想好好复习下《C语言程序设计》。
条件编译是对意思就是希望程序中一部分内容旨在满足一定条件时才编译,这对于处理器节省内存是很有用的。
具体的例子下次碰到再好好研究,呵呵~~
继续往下看是说宏#defineINT_PRIORITY_MASK
0xFF)
生成一个中断优先码,这个优先码是由硬件支持的优先级别。
算算这个INT_PRIORITY_MASK到底等于多少。
先要查出NUM_PRIORITY_BITS是多少,我猜应该在上文说道的hw_ints.h里面。
果不其然:
我用鼠标划过的就是它的出处,再返回去hw_ints.h看看它是何方神圣。
上面的英文说它是优先级的总数。
再结合上面的#defineNUM_INTERRUPTS
64
因为Cortex-M3有64个中断源,这样是为了方便统计。
同理#defineNUM_PRIORITY_BITS
3
也是对某种类型中断的统计并且这种中断有3个,但是具体哪三个我就真的不知道了。
查了datasheet也没发现。
先放在这里
,以后再来解决。
反正我知道NUM_PRITORITY_BITS就是3。
再回去算得INT_PRIORITY_MASK
是224。
这个224又有什么含义呢?
我找了好久的资料,还是一头雾水。
也先放着。
我们重点看下面的API函数原型。
看第一个:
它是一个外部函数,函数的返回值是tBoolean类型。
那么什么是tBoolean类型呢?
(字面意思是布尔型的)打开这个函数:
tBoolean
IntMasterEnable(void)
//
//Enableprocessorinterrupts.
return(CPUcpsie());
这是一个使能处理器中断的函数,ZLG是这么解释的:
如果在调用该函数之前处理器中断是使能的,则返回false;
如果在调用该函数之前处理器中断是禁止的,则返回true。
那么接下来:
这个函数就好理解了,它是一个禁止处理器中断的函数。
写到这里,我决定要先放一放这个
今天已经是学习LM3s1138的第三天了,通过前面两天的学习大概了解了这个M3处理器的一些寄存器信息。
今天努力把剩下的两个头文件搞定。
首先是sysctl.h头文件:
看头文件的名字就知道是系统管理的头文件。
这里宏定义了一大堆的系统控制寄存器的地址,这与前面的类似,不在多说。
我们先把这些地址放到一边,因为地址是不会变的。
我们关心的是它下面的API原型驱动函数的用法。
比较多,我们选择一部分进行分析。
系统控制总体上包含了:
LDO控制、时钟控制、复位控制、外设控制、睡眠与深度睡眠、杂项功能、中断操作、时钟验证。
一般我们只用前面6个,后面两个用的不多。
废话不多说了。
第一个:
LDO控制。
XX上面是这么描述的:
LDO是“LowDrop-Out”的缩写,是一种线性直流电源稳压器。
LDO的显著特点是输入与输出之间的低压差,能达到数百毫伏,而传统线性稳压器(如7805)一般在1.5V以上。
例如,Exar(原Sipex)公司的LDO芯片SP6205,当额定的输出为3.3V/500mA时,典型压差仅为0.3V,因此输入电压只要不低于3.6V就能满足要求,而效率可高达90%。
这种低压差特性可以带来降低功耗、缩小体积等好处。
芯片的datasheet上面是这么描述的:
Stellaris系列ARM集成有一个内部的LDO稳压器,为处理器内核及片内外设提供稳定的电源。
这样,只需要为整颗芯片提供单一的3.3V电源就能够使其正常工作,简化了系统电源设计并节省成本。
LDO输出电压默认值是2.50V,通过软件可以在2.25~2.75V之间调节,步进50mV。
降低LDO输出电压可以节省功耗。
LDO管脚除了给处理器内核供电以外,还可以为芯片以外的电路供电,但是要注意控制电流大小和电压波动,以免干扰处理器内核的正常运行。
片内LDO输入电压是芯片电源VDD(范围3.0~3.6V),LDO输出到一个名为“LDO”的管脚。
对于Fury和DustDevil家族(LM3S1000以上型号),LDO管脚要连接到内核电源VDD25管脚上,对于Sandstorm家族(LM3S1000以下型号)VDD25管脚是内置的,因此不必从外部连接。
注意:
在LDO管脚和GND之间必须接一个1~3.3μF的瓷片电容。
在启用片内锁相环PLL之前,必须要将LDO电压设置在2.75V。
到这里,我们对LDO的功能有了大致的了解。
但是具体输出LDO电压是哪个函数设置?
怎么设置?
LDO对应的寄存器地址是多少?
带着这些问题我们再看看上面列出来的函数吧。
直接看函数名肯定就是这三个了:
externvoidSysCtlLDOSet(unsignedlongulVoltage);
externunsignedlongSysCtlLDOGet(void);
externvoidSysCtlLDOConfigSet(unsignedlongulConfig);
第一个是设置LDO的输出电压,来看看具体的原函数:
void
SysCtlLDOSet(unsignedlongulVoltage)
//Checkthearguments.
ASSERT((ulVoltage==SYSCTL_LDO_2_25V)||
(ulVoltage==SYSCTL_LDO_2_30V)||
(ulVoltage==SYSCTL_LDO_2_35V)||
(ulVoltage==SYSCTL_LDO_2_40V)||
(ulVoltage==SYSCTL_LDO_2_45V)||
(ulVoltage==SYSCTL_LDO_2_50V)||
(ulVoltage==SYSCTL_LDO_2_55V)||
(ulVoltage==SYSCTL_LDO_2_60V)||
(ulVoltage==SYSCTL_LDO_2_65V)||
(ulVoltage==SYSCTL_LDO_2_70V)||
(ulVoltage==SYSCTL_LDO_2_75V));
//SettheLDOvoltagetotherequestedvalue.
HWREG(SYSCTL_LDOPCTL)=ulVoltage;
现在很好理解了,只要设置好ulVoltage的值就可以输出允许输出范围内的电压值了。
这个函数表面上看起来不难理解,但是具体ASSERT()代表什么意思呢?
没关系,XX一下。
搜到一篇这样的文章:
void
CRoundB
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- EasyARM1138 学习 过程