ARM入门笔记.docx
《ARM入门笔记.docx》由会员分享,可在线阅读,更多相关《ARM入门笔记.docx(13页珍藏版)》请在冰豆网上搜索。
ARM入门笔记
如果您是一个单片机爱好者,当见到一款功能强大、性价比高的处理器时,一定会有一股很想掌握它、运用它的冲动,起码我是这样。
5年前第一次接触单片机(标准的51系列),就被它强大的功能所吸引,而痴迷于它,一直到今天。
在这期间的不同时期,各种增强型51,PIC、AVR,DSP和ARM等不同程度的吸引和诱惑着我。
有的已经玩过了,有的则没有,但很想玩的这股冲动一直存在心里,特别是对ARM。
记得2002年我就知道了ARM这个东东,眼睁睁地看着它一天天的火热,但由于对其开发工具及开发过程的不了解等原因一直只处于认识的阶段,尽管也曾玩过ZLG的2104开发板!
可能是ZLG团队做的太出色的原因吧(详细的教材与源码,还有非常方便的工程模板),没过多久、没费多少劲就把里面的实验做完了,然后由于自已性格上的缺陷把它给扔在了一边,导致不到一个月就把大部分的东东还给了周老师。
直到前段时间,让我有机会真真正正的玩起了ARM!
像最初玩8051那样的尽兴(出现问题时,吃不下饭、睡不着觉的那种痛苦和解决问题后的那种畅快)。
这篇文档就是记录了我在前段时间学习、调试目前最低价的ARM核处理器——AT91SAM7Sxx时出现的问题与解决方法。
它尽量完整地记录了我从开始不懂ARM,到最终完成AT91SAM7S64各种外围实验的各个环节及整个过程,包括我在开始一个实验前的一些想法,实验目的,以及各个实验中,我以单片机的思维去思考时遇到的各种问题,和这些问题的解决方法。
现将自己的一点经验以及体会拿出来与大家共同分享,一来是希望能够为那些在ARM门口徘徊迷茫的人提供一些借鉴,使他们顺利越过这道门槛;二来是希望能够抛砖引玉,以结识更多有共同爱好的朋友。
由于我也是新手上路,文章中难免疏漏与错误,希望大家不吝指正,如果在调试AT91SAM7Sxx时有什么问题,欢迎大家共同讨论。
第一章 开发工具与调试环境
一.目标板
所用实验板是参考ATMEL公司官方网站上发布的《AT91SAM7S-EKEvaluationBoardUserGuide》设计的,相当于AT91SAM7Sxx评估测试板,主要用于各种外围实验,结构框图如图1-1所示。
大家可以自己搭板子,或者直接购买现成的AT91SAM7Sxx评估板。
第二章我的第一个实验――将程序执行到C文件的main函数
二.实验目的
运用ADS编写一个小程序,使程序能够从起始的汇编代码运行到C程序的main()函数(这也可称作非常简单的起动代码),并通过仿真器连接目标板,最终能够在AT91SAM7S64里正确运行。
三.实验程序和参数设置
1>连接器的选项设置
选项设置如图2-1所示。
因为在AT91SAM7S64中FLASH存储器的地址是以0x0开始,而SRAM的地址是以0x00200000开始,所以我将下图中的ROBase和RWBase分别设置成了0x0和0x00200000。
其它设置请参考有关书籍。
第三章点亮我的LED
――I/O输出实验
一.背景
当完成上述实验后,我就可以像使用51单片机那样,在C文件的main()函数中通过设置相应的寄存器来达到对相应外设(如I/O的输入、输出等)的控制目的。
二.实验目的
通过控制PIO的相关寄存器,使特定的I/O口输出高电平和低电平,来点亮LED。
三.实验程序和参数设置
1>连接器选项设置
ROBase=0x00200000;
RWBase=0x00202000;
Imageentrypoint=0x00200000;
2>启动代码
启动代码与第一个实验中修改后的相同,即将“__main()”改成main()。
3>C语言的代码
#include"AT91SAM7S64.h" //特殊功能寄存器头文件。
类似与51单片机中reg51.h
#include"Board.h" //定义目标板的头文件
intmain(void)
{*AT91C_PIOA_PER=LED_MASK;//使能4个LED对应管脚的I/O口功能
*AT91C_PIOA_OER=LED_MASK;//使能4个LED管脚的输出功能
while
(1)
{//可用单步运行来查看输出结果
*AT91C_PIOA_SODR=LED1;//将LED1对应的管脚输出高电平
*AT91C_PIOA_CODR=LED1;//将LED1对应的管脚输出低电平
}
}
四.总结
本实验非常的顺利,没有出现问题。
证明ARM芯片的内部外围与8位单片机内部外围的控制或使用方法在原理上基本是相同的,都是通过设置相关的特殊功能寄存器来实现控制。
也就是说只要会单片机也就会ARM!
第四章键盘输入
——I/O输入实验
一.实验目的
能够正确读取PIO的管脚状态,实现当有按键按下时,LED点亮,否则灭。
二.实验程序和参数设置
1>连接器选项设置和启动代码都与上个实验相同
2>C语言的代码
#include"AT91SAM7S64.h"//特殊功能寄存器头文件。
类似与51单片机中reg51.h
#include"Board.h" //定义目标板的头文件
intmain(void)
{unsignedintKey_Val; //定义变量,unsignedint为32位,C51是16位
*AT91C_PIOA_PER=LED_MASK|SW_MASK;//使能LED、KEY脚I/O口功能 *AT91C_PIOA_ODR=SW_MASK;//使能4个KEY对应管脚的输入功能
*AT91C_PIOA_OER=LED_MASK;//使能4个LED管脚的输出功能
while
(1)
{
Key_Val=*AT91C_PIOA_PDSR;//读管脚的电平状态
if(Key_Val&SW1)
{
*AT91C_PIOA_SODR=LED1;//将状态反映给LED
}
else
{
*AT91C_PIOA_COER=LED1;//按钮按下时,LED1亮
}
}
}
三.出现的问题与解决方法
1> 无论作输入用的I/O口电平如何变化,管脚状态寄存器(AT91C_PIOA_PDSR)的内容始终为0,即I/O口的输入功能没起作用。
原因是AT91SAM7Sxx内部集成了功率管理控制器,用它来控制所有外设的时钟以达到优化功耗的目的。
所以只有使能了PIO的外围时钟,PIO外设才会工作,才能读入输入管脚的状态。
那么为什么PIO作为输出时不需要使能外围时钟呢?
我个人认为这与内部外设在数字电路上的实现有关,输出功能只需要组合逻辑电路(不用时钟)就能实现,而输入功能则需要用到时序逻辑电路(需要时钟)才能实现。
因此,必须在main()函数的开头增加如下两条时钟使能的语句:
*AT91C_PMC_SCER=AT91C_CKGR_MOSCEN;//使能系统时钟寄存器的处理器时钟
*AT91C_PMC_PCER=191C_ID_PIOA;//使能PIOA外围时钟
四.总结
当要使AT91SAM7S64特定的外设工作时,必须使能相应外设的时钟控制寄存器。
相反,如果不用相应的外设,禁能相应的时钟可以降低功耗。
第五章模拟量输入
——A/D转换实验
一.目的
通过A/D转换,能够正确读取外部模拟输入通道的电压值。
二.实验程序和参数设置
1>连接器选项设置和启动代码都与上个实验相同
2>C语言的代码
#include"AT91SAM7S64.h"
#include"Board.h"
volatileunsignedintEXT_AD_Val[4]; //定义A/D转换结果值
intmain(void)
{unsignedchari;
*AT91C_ADC_CR=AT91C_CKGR_MOSCEN;//ADC的软件复位,即清除ADC的所有寄存器
*AT91C_PMC_SCER=AT91C_CKGR_MOSCEN;//使能系统时钟寄存器的处理器时钟
*AT91C_PMC_PCER=191C_ID_ADC;//使能ADC时钟
*AT91C_PIOA_PDR=EXT_AD0|EXT_AD1;//禁止管脚的I/O口功能,使作为模拟输入功能
*AT91C_ADC_MR =0x0f1f3f00; //软件起动,10位分辨率,128分频
*AT91C_ADC_CHER=0x33; //使能通道0,1,4,5
*AT91C_ADC_CHDR=0xcc; //禁能通道2,3,6,7
*AT91C_ADC_IDR =0xfffff; //禁止所有ADC中断
while
(1)
{*AT91C_ADC_CR=0x2; //起动转换
while
(1)
{if((*AT91C_ADC_SR)&0x33) //等待转换结束
{EXT_AD_Val[0]=(*AT91C_ADC_CDR0)&0x3ff;//读取10Bit的结果值
EXT_AD_Val[1]=(*AT91C_ADC_CDR1)&0x3ff;
EXT_AD_Val[2]=(*AT91C_ADC_CDR4)&0x3ff;
EXT_AD_Val[3]=(*AT91C_ADC_CDR5)&0x3ff;
for(i=0;i
break;
}
}
}
}
三.总结
执行ADC的软件复位,将清除ADC的所有相关寄存器,因此必须在设置ADC相关寄存器之前执行。
第六章RS232串口通信
——串口USART0通讯实验
一.实验目的
利用串口调试软件能够正确接收到AT91SAM7S64发出的数据,AT91SAM7S64也能正确接收到调试软件发出的数据。
二.实验程序和参数设置
1>连接器选项设置和启动代码都与上个实验相同
2>C语言的代码
#include"AT91SAM7S64.h"
#include"Board.h"
unsignedcharRBuff[256]; //定义接收缓冲区
unsignedcharindex;
intmain(void)
{
unsignedinti,delay;
*AT91C_CKGR_MOR=0x701;//使能主振荡器和设置起振时间
*AT91C_PMC_MCKR=0x01;//选择MsterClockismainclock,dividedby0
*AT91C_PMC_SCER=AT91C_CKGR_MOSCEN;//使能系统时钟寄存器的处理器时钟
*AT91C_PMC_PCER=AT91C_ID_US0;//使能USART0时钟
*AT91C_PIOA_PDR=US_RXD_PIN|US_TXD_PIN;//禁止该两个管脚的I/O口功能
*AT91C_PIOA_ASR=US_RXD_PIN|US_TXD_PIN;//将该两个I/O口分配给外围A
*AT91C_US1_MR=0x8c0; //正常模式,时钟为MCK,8位长度,无校验,1位停止位,
*AT91C_US0_IDR =0xf3fff;//禁止所有UART相关的中断
*AT91C_US0_BRGR=30; //设置波特率为38400Hz,AT91C_US0_BRGR为CD值
//Baudrate=SelectedClock/(8(2-Over)CD)=MCK/16CD=18432000/(16*30)=38400
*AT91C_US0_CR =0x15c;//复位接收器、发送器和状态位;使能接收与发送
index=0;
while
(1)
{
for(i=0;i
{//发送程序
if((*AT91C_US0_CSR)&AT91C_US_TXEMPTY)//判断发送器是否为空
{
*AT91C_US0_THR=i;//空,则发送数据
}
for(delay=0;delay
}
if((*AT91C_US0_CSR)&AT91C_US_RXRDY)
{//接收程序,在调试该部分时,要将发送部分程序注示掉
RBuff[index++]=*AT91C_US0_RHR;
}
}
}
三.出现的问题与解决方法
1> 状态寄存器中的发送准备位(TXRDY)和发送空标志位(TXEMPTY)一直为0,表示发送器未准备好和缓冲区不空。
原因是发送器复位后还未使能。
不能同时进行发送器(或接收器)复位与使能操作(*AT91C_US0_CR=0x15c),这样使能操作会无效,必须将它们分开,即先进行复位(*AT91C_US0_CR=0x10c),再进行使能(*AT91C_US0_CR=0x50)。
2> 串口接收、发送的数据不对
原因是系统主时钟和分频后的时钟计算错误,引起波特率也计算错误。
很有必要深入研究关于时钟的产生、分频及波特率计算等内容。
3> 每次从串口调试软件收到的数据中,低四位正确,高四位错误。
原因是将“*AT91C_US0_MR=0x8c0;”写成了“*AT91C_US1_MR=0x8c0;”,而引起通讯模式根本不对。
可以说这是一个非常低级的错误,但它却花费了我很久的时间才找到症结所在。
在找原因的过程中,使我对串口相关的(如各种时钟的产生、波特率的计算等)内容有了更深刻的理解。
四.总结
在本实验中串口为异步模式,波特率的计算如下式所示:
Baudrate=SelectedClock/(8(2-Over)CD)
其中在USART模式寄存器(AT91C_US0_MR)中设置SelectedClock为MCK;Over为1则上式变成如下所示:
Baudrate=SelectedClock/(8(2-Over)CD)=MCK/16CD
在MasterClockRegister(AT91C_PMC_MCKR)中将MCK设置为MainClock且不分频,即为外部振荡时钟(接在XIN和XOUT管脚间的晶振)的频率,因为外部晶振是18.432MHz,所以MCK就为18432000,则上式变成如下所示:
Baudrate=SelectedClock/(8(2-Over)CD)=MCK/16CD=18432000/(16*30)=38400
串口DMA控制实验
一.背景
DMA是DirectMemoryAccess的缩写,即“存储器直接访问”。
它是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,即不通过CPU,也不需要CPU干预。
整个数据传输操作在一个称为“DMA控制器”的控制下进行的。
CPU除了在数据传输开始和结束时作一点处理外,在传输过程中CPU可以进行其它的工作。
这样,在大部分时间里,CPU和输入输出都处在并行操作。
因此,使整个计算机系统的效率大大提高。
AT91SAM7S64串口外围DAM控制器的工作过程:
将要发送的数据缓冲区的起始地址赋给串口DMA控制器的发送指针寄存器,再将要发送的字节个数赋给PDC的发送计数寄存器,然后无须CPU的干预,DMA自动起动串口发送操作,发送完这些数据后又自动停止;同理,只要将接收数据缓冲区的起始地址赋给串口DMA控制器的接收指针寄存器,再将要接收的字节个数赋给PCD的接收计数值,DMA将自动启动串口接收数据,接收完这些数据后,再通知CPU。
二.实验目的
验证上述所描述的串口DMA控制器的工作过程,可用串口调试软件进行验证。
三.实验程序和参数设置
1>连接器选项设置和启动代码都与上个实验相同
2>C语言的代码
#include"AT91SAM7S64.h"
#include"Board.h"
unsignedcharRxBuff[256],TxBuff[256];
intmain(void)
{
unsignedinti;
*AT91C_CKGR_MOR=0x701;//使能主振荡器和设置起振时间
*AT91C_PMC_MCKR=0x01;//选择MsterClockismainclock,dividedby0
*AT91C_PMC_SCER=AT91C_CKGR_MOSCEN;//使能系统时钟寄存器的处理器时钟
*AT91C_PMC_PCER=AT91C_ID_US0;//使能USART0时钟
*AT91C_PIOA_PDR=US_RXD_PIN|US_TXD_PIN;//禁止该两个管脚的I/O口功能
*AT91C_PIOA_ASR=US_RXD_PIN|US_TXD_PIN;//将该两个I/O口分配给外围A
*AT91C_US0_CR=0x1ac; //复位接收器和发送器,使能接收与发送,复位状态位
*AT91C_US1_MR=0x8c0; //正常模式,时钟为MCK,8位长度,无校验,1位停止位,
*AT91C_US0_IDR =0xf3fff;//禁止所有UART相关的中断
*AT91C_US0_BRGR=30; //设置波特率为38400Hz,AT91C_US0_BRGR为CD值
*AT91C_US0_CR =0x50;//使能发送与接收
*AT91C_US0_PTCR=AT91C_PDC_TXTEN|AT91C_PDC_RXTEN;//使能US0的PDC发送与接收
for(i=0;i //给发送缓冲区覆值
TxBuff =i;
} //下面可用单步执行,来观察现象
*AT91C_US0_TPR=(unsignedint)TxBuff;//覆发送缓冲区起始地址
*AT91C_US0_TCR=256; //起动PDC发送256个字节
*AT91C_US0_RPR=(unsignedint)RxBuff;//覆接收缓冲区起始地址
*AT91C_US0_RCR=256; //开始PDC接收
while
(1);
}
四.总结
我们在用51等单片机的串口进行收发数据时,因为发送与接收共用一个Buffer,所以在发送一字节数据后,通常都要加“while(!
TI);”语句,来等待数据发送完毕;在接收数据时都要使用中断来处理,每当接收到一个字节数据后都要中断一次CPU。
有了DMA这个功能,就不用这样浪费CPU的时间,可大大的提高CPU的实时性能。
第八章中断控制实验
一.背景
实际上ARM的中断与51单片机的中断类似,都有类似的中断入口地址(ARM称异常向量表)。
只不过51给两个相互的中断入口之间留有足够的空间(如外部中断0的中断入口在03H处,而定时器0的中断入口在0BH处),在这段空间中可以放多条指令,这样在编写中断处理程序时非常灵活。
ARM总共有7种中断(或异常)类型,它们的入口分别为00H、04H┄┄1CH,入口与入口之间只够放一条指令,这条通常为“B XX”或者“LDR PC,ResetAddr”的跳转指令。
二.实验目的
在IRQ中断向量地址(0x18)处设置一个断点后全速运行,用按钮产生PIO中断输入信号,使产生中断,而跳转到设置的断点处。
三.实验程序和参数设置
1>连接器选项设置与上个实验相同
2>启动代码
与前几个实验相比,使能了IRQ中断后再跳到C语言的主函数。
AREAinit,CODE,READONLY
CODE32
Mode_USREQU 0x10;CPSR中各种处理器模式对应的控制位
USR_StackEQU 0x00204000;定义RAM的最高地址,无重映射
ENTRY
B InitReset ;0x00Resethandler
Undefvec B undefvec ;0x04UndefinedInstruction
swivec B swivec ;0x08SoftwareInterrupt
pabtvec B pabtvec ;0x0CPrefetchAbort
dabtvec B dabtvec ;0x10DataAbort
rsvdvec B rsvdvec ;0x14reserved
irqvec B irqvec ;0x18IRQ
fiqvec B fiqvec ;0x1cFIQ
InitReset
MSRCPSR_c,#Mode_USR ;使能FIQ,IRQ中断
LDRSP,=USR_Stack
IMPORT main
b main
END
3>C语言代码
#include"AT91SAM7S64.h"
#include"Board.h"
unsignedintKey_Val;
unsignedintkey;
intmain(void)
{
*AT91C_PMC_SCER=0x1; //使能系统时钟寄存器的处理器时钟
*AT91C_PMC_PCER=191C_ID_PIOA;//使能PIOA外围时钟
*AT91C_PIOA_PER=SW_MASK; //使能KEY引脚的I/O口功能
*AT91C_AIC_IDCR=191C_ID_PIOA;//禁止PIO外围中断功能
*AT91C_PIOA_ODR=SW_MASK; //使能4个KEY管脚的输入功能
AT91C_BASE_AIC->AIC_SMR[AT91C_ID_PIOA]=AT91C_AIC_PRIOR_HIGHEST|AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED;//中断模式(优先级和触发模式)
*AT91C_AIC_ICCR=191C_ID_PIOA;//中断清除
*AT91C_PIOA_IDR=0xffffffff; //禁止所有PIO口的中断功能
*AT