ARM4412

ARM基础

ARM认识

嵌入式产品
ARM产品线
FLASH相当于电脑的硬盘,内存相当于电脑的运行内存,控制器搬移这些运算指令,(ALU)运算机进行解析,寄存器用于临时存放用于运算的数据
ARM系统硬件组成和运行原理

ARM开发环境

  1. 安装交叉编译工具链 我们用的交叉编译工具链是arm-2011.09-70-arm-none-linux-gnueabi.exe
    在该可执行文件上右键->属性 -> 兼容性 选择兼容window 7
    确定后以管理员身份运行即可,可能需要很长时间。
    注意:win8运行会报错。
  2. 安装keil(MDK) 直接运行ARM环境\ARM-tools\mdk454_mcu123\MDK454
    安装路径选择默认,一路next,会花费很长时间。
  3. 创建一个project
    3.1 新建一个文件夹(keil_proc/test),用于存放工程。
    3.2 新建一个工程,Project -> New…, 然后选择型号,arm9(小端)
    3.3 在工程里面添加或者new一个文件(start.s),在工程管理区的Source Group右键, 选择add group … 选择自己 新建的文件,或者是其他文件添加到工程里面.
    3.4. 可以通过Target -> Manage components去修改工程和groups的名字,或者批量添加文件
  4. 关联arm-none-linux-gnueabi工具链 Project -> Manage-> Components … -> Folders/Extensions 做如下操作
    在这里插入图片描述
  5. 导入链接脚本 Project -> Options for Target … -> Linker
    设置Linker Scipt file为工程目录下的map.lds(需要先将ARM环境\ARM-tools\里面的map.lds放到新建的工程里面即 keil_proc/test里面)
  6. 写代码
    .text .
    global _start
    _start:
    movr1, #0xff
    stop:
    b stop
    .end
  7. 编译和调试
    在这里插入图片描述
    分别为编译单个文件和多个文件
    在这里插入图片描述
    调试

    ARM的工作模式及寄存器

    ARM工作模式
  • ARM主要有7个基本工作模式
    User : 非特权模式,大部分任务执行在这种模式
    FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式
    IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式
    Supervisor :当复位或软中断指令执行时将会进入这种模式
    Abort : 当存取异常时将会进入这种模式
    Undef : 当执行未定义指令时会进入这种模式
    System : 使用和User模式相同寄存器集的特权模式
  • ARM有37个寄存器(存放数据与指令)
    1个用作PC( Program Counter)
    1个用作CPSR(Current Program Status Register)
    5个用作SPSR(Saved Program Status Registers)
    30 个通用寄存
    ARM工作模式及寄存器框图
    搬移指令
  • mov r1,#3
  • mov r2,r1
    CPRS寄存器
    在这里插入图片描述
    条件位:
    N = Negative result from ALU
    Z = Zero result from ALU
    C = ALU operation Carried out or borrow
    V = ALU operation oVerflowed

T 位 J 位
T = 0;J=0 处理器处于 ARM 状态
T = 1;J=0 处理器处于 Thumb 状态

中断禁止位:
I = 1: 禁止 IRQ.
F = 1: 禁止 FIQ

Mode位:处理器模式位
10000 User mode 10011 SVC mode;
10010 IRQ 10001 FIQ mode;
10111 Abort mode 11011 Undfined mode 11111 System mode;

CPSR / SPSR操作指令
mrs r0,CPSR
msr CPSR,r0

ARM指令

搬移指令

  • mov r13,#3 //将立即数3赋值给寄存器r13
  • mov r0,r1//将r1中的数赋值给r0
  • mov r0,r1,LSL#2 //将r1中的数逻辑左移2两位到r0
  • mov r0,r1,LSR#2
  • mrs r0,cpsr //将cpsr读到寄存器r0中
  • msr cpsr,r0//将寄存器r0的值读出到cpsr
    条件执行
    @if (a==0) x=0;//@表示汇编语言的注释
    @if (a>0) x=x+3;
    mov r0,#0//赋值
    cmp r0,#0//比较指令
    moveq r1,#0//对前面一次结果进行判断,如果相等,将立即数赋值给r1
    addgt r1,r1,#3//对前面一次结果进行判断,如果大于0,将r1加3赋值给r1
    指令时如何存储的,如何呗解析的
    指令机器码
    逻辑指令
  • and r0,r1,#0xFF // r0 = r1&0xFF
  • orr r3,r0,#0x0F // r3 = r0|0x0F
  • bic r0,r0,#0x03 // 清除r0中的0号位和1号位
  • tst r0,#0x20 //测试第6位是否为0 ,为0则Z标志置1
  • cmp r1,r0 //将R1与R0相减做比较,并根据 结果设置CPSR的标志位
    算术指令
  • add r0,r1,r2 //r0=r1+r2
  • sub r0,r1,#3 //r0= r1 - 3
  • sub r0,r1,r2,LSL#1 //r2左移一位ie,r1-r2,然后赋值给r0
  • mul r1,r2,r3 //r1=r2*r3
    跳转指令
  • b main //跳转到标号为main地代码处
  • bl func //保存下一条要执行的指令的位置到 LR寄存器,跳转函数func
    //当跳转代码结束后,用MOV PC,LR指令跳回来
  • beq addr //当CPSR寄存器中的Z条件码置位时,跳转到该地址处(相等)
  • bne addr //当不等时,跳转到地址addr(不相等)
@ 实现 延时1秒函数
@delay fos 1 second
	.text
main:
	bl delay1s
main end:
	b	main_end
delay1s:	
    ldr    r4,=0x3FFFF
loop_delay1s:
    sub    r4,r4,#1
	 cmp    r4,#0         
	 bne    loop_delay1s
delay1s_end:
    mov    pc,lr

	.end
@用汇编实现求最大公约数?(如9 15 值是3) 

int GCD(int a,int b)
{	 	  
 	 while(1)
 	 {
       if(a==b)
         break; 	
       if(a>b){
	       a=a-b;
       }else{
          b=b-a;  	
       }	
    } 		
    return a;	 	   
} 	

Load/Store 指令
注:load/store架构规定,存储器之间不能直接拷贝(FLASH不能直接到内存单元中),需通过寄存器做中转

ldr r0,[r1] (load) //r0=*r1 r1里存放的是地址,把该地址里存放的内容读入到r0中
//LDRB(byte) LDRH(half word)

ldr r0,[r1,#8] //r0=*(r1+8) 存储器地址为r1+8的字数据读入寄存器0。
ldr pc,_irq // pc = *(_irq) 将标号中的内容放入pc中

str r0,[r1] (store) // *r1 = r0 将寄存器r0中值写入到存储器地址为r1的空间中

str r0,[r1],#4 // r0=*r1, r1=r1+4 将r0 中的字数据写入以r1为地址的内存中,并将新地址r1+4 写入r1

str r0,[r1,#4] //*(r1+4)=r0 将r0 中的字数据写入以r1+4 为地址的内存中
Pre or Post Indexed(前或后索引) 寻址
前索引与后索引

@拷贝srcBuf里内容 到destBuf中
	   .text(代码段,数据只能读)
     ldr  r0,=srcBuf  //将FLASH数据读入到寄存器中
     ldrb  r1,[r0] 
     ldr  r0,=destBuf	//将寄存器中数据存入到内存单元中
     strb  r1,[r0]
  srcBuf:  
     .byte  0x01,02,0x03,0x04 
	  .data(数据段,数据可读可写)
destBuf:
      .space 8
	  .end
/*用汇编实现下面功能     	
        main()
     	{
            int i=0;
	    const  char buf[]={1,2,3};
	    char destBuf[8];
            for(i=0,i<3,i++)
            {
		destBuf[i] = buf[i];
	    }
	}
*/

		 .text(代码段,数据只能读)
 
main:
	mov r5,#0  
     ldr  r7=Buf  //将FLASH数据读入到寄存器中 
     ldr  r8,=dest_Buf	//将寄存器中数据存入到内存单元中
loop:
	cmp r5,#3
	beq	main_end
	ldrb	r0,[r7],#1
	strb	r0,[r8],#1  
  	b	loop
  	
main_end:
	b	mian_end
	  	
Buf:  
     .byte	1,2,3 
	  .data(数据段,数据可读可写)
dest_Buf:
      .space 8

		.end

GUN汇编伪指令
.text 将定义符开始的代码编译到代码段
.data 将定义符开始的代码编译到数据段

.end 文件结束

.equ GPG3CON, 0XE03001C0 定义宏(即用GPG3CON代替 0XE03001C0)
.byte 定义变量 1字节
.byte 0x11,‘a’,0 定义字节数组
.word 定义word变量 (4字节 32位机)
.word 0x12344578,0x33445566
.string 定义字符串 .string “abcd\0”

ldr r0,=0xE0028008 载入大常数0xE0028008 到r0中
.global _start 声明_start 为全局符号

@拷贝ROM中字符串到RAM中
   .text
start:
    ldr  r5,=srcBuf
    ldr  r6,=destBuf
loop:
    ldrb r4,[r5]
    cmp  r4,#0
    beq  main_end
    ldrb  r0,[r5],#1
    strb  r0,[r6],#1	
    b   loop

main_end:
    b    main_end
srcBuf:
    .string  "abcdefg\0"
    .data 
destBuf:
    .space  8
	.end

批量操作
批量操作指令 (ia-Increment After ib-Increment Before da-Dec After db-Dec Before)
ldmia r0!, {r3 - r10} //r0里地址指向的内容批量,load 到r3~r10寄存器中, r0里地址会自动加4
stmia r0!, {r3 - r10} //把r3~r10寄存器中内容,store 到r0里地址执行空间中,r0里地址会自动加4

@例:实现块数据批量拷贝
   @r12指向源数据起始地址   
   @r14指向源数据尾地址
   @r13指向目的数据起始地址

   .text
   ldr r12,=srcBuf
   ldr r13,=dstBuf
   ldmia  r12!,{r0 - r11}
   stmia  r13!,{r0 - r11}

  .data
srcBuf:
  .string "abdfasdf13535dfksjdlfkjlksldkjflkl\0"
srcBuf_end:  

dstBuf:
  .space  12*4
  .end

堆栈操作指令

  • stmfd sp!,{r0-r12,lr}
    将寄存器r0~r12 lr中的值存入栈中
    常用于中断保护现场,! 表示会自动偏移(栈空间地址会进行减法,如果存入一个寄存器,则栈地址减去4个字节)
  • ldmfd sp!,{r0-r12,pc}^
    将栈中值逐个弹出到寄存器r0~r12 pc中
    常用于中恢复断现场,^表示会恢复spsr到cpsr
    堆栈操作
    ARM指令高级
  • 软中断指令
    swi 0x02 产生软中断, 软中断号为2 SWI,软中断

ARM–异常处理

认识异常

异常向量表
异常处理过程
异常处理过程

  • 异常与工作模式关系:
    复位,软中断 -->SVC
    Prefetch(预取),Data Abort -->Abort
    其他中断对应对应名字的模式

    user system 模式不是异常触发切换的,是程序员修改CPSR,实现切换的
    异常是随机的

软中断处理程序

@ 在用户模式下触发6号软中断,在6号软中断中修改寄存器的值,最后返回到用户模式

	.text
	.global _start
_start:

	b reset
	ldr pc, _undefined_instruction    // pc = *(_undefined_instruction)  将标号中的内容放入pc中,相对于 b指令,突破了32M空间的限制。
	ldr pc, _software_interrupt
	ldr pc, _prefetch_abort
	ldr pc, _data_abort
	ldr pc, _not_used
	ldr pc, _irq
	ldr pc, _fiq

_undefined_instruction:
	.long _undefined_instruction  //定义long变量
_software_interrupt:
	.long software_interrupt
_prefetch_abort:
	.long _prefetch_abort
_data_abort:
	.long _data_abort
_not_used:
	.long _not_used
_irq:
	.long _irq
_fiq:
	.long _fiq

reset:

	/*初始化栈指针*/
	ldr sp, = stack_base   

	/*切换为用户模式*/
	mrs r0, cpsr
	bic r0, #0x1f
	orr r0, #0x10
	msr cpsr, r0
	
	swi  0x06    @触发软中断异常
						@保存返回地址到lr(返回地址为下一条指令,该指令地址等于lr-4)
						@切换到SVC工作模式
	nop
	nop
	
stop:
	b stop
do_swi:
	mov r3, #3
	mov r4,	#4
	mov pc, lr

software_interrupt:

	@如果是IRQ或者FIQ:  sub lr ,#4
	
	stmfd sp!, {r0-r12,lr}  @保护现场
	sub lr, #4				@取出软中断号,并且比较软中断号
	ldr r0, [lr]
	bic r0, #0xff000000
	cmp r0 ,#0x6
	bleq do_swi				 @处理软中断
	
	ldmfd sp!, {r0-r12,pc}^  @恢复现场(^表示会恢复spsr到cpsr)
	


stack_end:
	.space 200
stack_base:	
	
	.end

C和汇编混合编程

裸机开发

开发板
裸机驱动的开发步骤
//最简例子 LED灯控制

  1. 看电路图
    a. 找到我要控制的设备
    b. 找到设备在CPU侧的控制管脚(如GPX2_7)
  2. 看芯片手册 (先看相关的中文文档,熟悉设备,再看手册)
    a. 搜索电路图里对应控制管脚的名称(如GPX2)
    b. 看目录找到对应的控制模块(如:6 General Purpose Input/Output (GPIO) Control)
    c. 看该模块的overview 了解该模块的大概功能
    d. 看控制寄存器(REGISTER DESCRIPTION) 重点,难点
    注: 如果寄存器比较多什么办?
    看技术支持提供的例子程序, 找到需要修改的寄存器(常常只有几个)。
    部分厂商会提供配置软件,通过界面去配置功能, 我们只需使用配置好的寄存器值就可以了
  3. 编程
    a. 定义要控制的寄存器的宏 (与手册里的寄存器地址对应起来)
    b. 设备初始化 (如设置GPIO为输出状态)
    c. 把功能分成最基本的小块,逐个实现 如点亮灯-> 在灭灯-> 加延时->闪烁-> 跑马灯
    SOC片上系统
@LED灯延迟一秒闪烁控制程序
.equ  GPX2CON,0x11000C40
.equ  GPX2DAT,0x11000C44
   .section .text  
   .globl _start
_start:
      @set Pin OutPut(设置GPX2_7管脚为输出状态)
      ldr   r0,=GPX2CON    //将GPX2_7管脚地址赋值给通用寄存器r0
      ldr   r1,[r0]      //将r0地址里面存放的内容放入r1中
      bic   r1,#0xF0000000	//将GPX2_7管脚清零
      orr   r1,#0x10000000		//将GPX2_7置为1
      str   r1,[r0]  	//将r1的值赋给地址为r0的空间
loop:
	@set Pin High Level for led on(设置GPX2_7为高电平)
	ldr   r2,=GPX2DAT
	     ldr   r3,[r2]
	     orr   r3,#0x80
	     str   r3,[r2]
		
		bl		delay1s //保存下一条要执行指令的地址,跳到函数delay1s中

	@set Pin low Level for led on
		 ldr   r2,=GPX2DAT
	     ldr   r3,[r2]
	     bic   r3,#0x80
	     str   r3,[r2]

		bl		delay1s
		b		loop    //直接跳到函数loop
		
delay1s:
     ldr      r4,=0x1ffffff   //用伪指令将较大数值数值付给r4
delay1s_loop:
     sub    r4,r4,#1		//将r4的值-1后赋值给r4
     cmp   r4,#0         //比较r4与0
     bne    delay1s_loop	//比较结果不相等,则跳到函数delay1s_loop
     mov   pc,lr		//将lr地址赋给pc
   .end
 @Makefile编译程序
 CROSS = arm-none-linux-gnueabi-     注:指定交叉编译工具
 CC=$(CROSS)gcc 
 LD=$(CROSS)ld
 OBJCOPY=$(CROSS)objcopy
all: led.s
         $(CC) -g -c -o led.o led.s  
         $(LD) led.o -Ttext 0x43e00000 -o led.elf    
               注: -Ttext 指定链接地址为 0x43e00000  
        $(OBJCOPY)  -O binary -S led.elf led.bin  
               注:转换为 binary 格式的,这样在u-boot中才能直接运行
        $(CROSS)objdump -D led.elf > led.dis      
               注:objdump	-D 反汇编生成文件 led.dis  
clean:
        rm -f *.o *.elf *.bin *.dis 

Linux开发环境搭建

  1. 虚拟机与window建立文件共享
    VM -> setting -> Options -> ShareFolder->add E盘 -> Always enable
    $cd /mnt/hgfs/E 该目录就是共享目录
  2. 交叉编译工具安装
    拷贝gcc-4.6.4.tar.xz到linux系统中
    $ tar xvf gcc-4.6.4.tar.xz
    $ export PATH=xxx/gcc-4.6.4/bin:$PATH
    xxx是 gcc-4.6.4解压后所在的目录
    $ arm-n 按Tab键能补全为
    arm-none-linux-gnueabi- 表示安装成功
  3. 看到串口信息
    连接串口线,安装串口驱动
    打开超级终端 选择对应串口
    串口设置为 115200 flow control = none
    重上电能看到串口信息
  4. 用串口传输文件
    串口终端里输入
    loadb 40001000
    再选传送-> 发送文件->选择待发送的文件
    如led.bin
    选协议为kermit ,点传送
  5. 运行裸机程序
    go 40001000

通讯接口

设备间通讯实现

在这里插入图片描述
串行通信与并行通信的区别:串行通信只是用一根信号线传输数据,每次传输一位,串行传输的特点是线路简单成本低。而并行通信是使用多根信号先进行信号传输,相当而言并行传输更开快。
同步与异步的区别:接受与发送使用的是否是同一时钟

UART编程

  • 初始化
    管脚设置为UART模式(com2口对应TXD2、RXD2)
    串口协议设置(奇偶校验位,数据位等)
    串口波特率设置
  • 发送字符
    发送状态判断
    发送
  • 接收字符后环回
    接收状态判断
    接收
/*UART程序*/
typedef struct {
   unsigned int GPA1CON;   //配置管脚模式  -- uart
   unsigned int GPA1DAT;   
}gpa1;
#define GPA1 (*(volatile  gpa1*)0x11400020)

typedef struct {
	unsigned int ULCON2;  //设置uart 帧格式 
	unsigned int UCON2;   //设置uart工作模式 -- poll 
	unsigned int UFCON2;
	unsigned int UMCON2;
	     unsigned int UTRSTAT2; //查询发送和接收状态	
     unsigned int UERSTAT2;
	unsigned int UFSTAT2;
	unsigned int UMSTAT2;
	unsigned int UTXH2;  //发送(写)
	unsigned int URXH2;  //接收(读)
      unsigned int UBRDIV2;  //设置波特率整数部分
      unsigned int UFRACVAL2;  //设置波特率小数部分
	unsigned int UINTP2;
	unsigned int UINTSP2;
	unsigned int UINTM2;
}uart2;
#define UART2 ( * (volatile uart2 *)0x13820000 )

void uart_init()
{
  //-----管脚配置为uart 模式	 
  GPA1.GPA1CON = (GPA1.GPA1CON & ~0xFF ) | (0x22);       //GPA1_0:RX;GPA1_1:TX
  //-----Uart 模块的设置
 //设置uart 帧格式 为8 data bit 1 stop ,none pairty  
  UART2.ULCON2 =0x03;
 //设置uart 发送和接收为普通的polling 模式
   UART2.UCON2 = 0x5;		 
   /*设置uart 波特率为115200
* Baud-rate 115200: src_clock:100Mhz
* DIV_VAL = (100*10^6 / (115200*16) -1) = (54.3 - 1) = 53.3
		  * UBRDIV2 = (Integer part of 53.3) = 53 = 0x35
	  * UFRACVAL2 = 0.3*16 = 0x5
	  * */
	 UART2.UBRDIV2 = 0x35;
	 UART2.UFRACVAL2 = 0x5;
}
void putc(const char c)
{
        //检测发送缓存为空,则写入数据。否则死循环	
     while(!(UART2.UTRSTAT2 & 0X2)) ;
         UART2.UTXH2 = c;
}
/*链接脚本map.lds*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
    . = 0;
    . = ALIGN(4);
    .text :
        {
        start.o(.text)
        *(.text)
    }
    . = ALIGN(4);
    .data : 
    { *(.data) }
    . = ALIGN(4);
    .bss :
    { *(.bss) }
}

中断机制

在这里插入图片描述

中断过程

  1. 中断初始化
    a. 管脚初始化
    b. 中断控制器初始化
  2. 中断向量表
    a.中断发生后,硬件自动跳转
    b. 现场保护
    c. 调用中断处理
  3. 中断处理
    a. 根据中断号做相应处理
    b. 清中断
    c. 现场恢复
    在这里插入图片描述

协处理器改变内核访问的基地址

  • 协处理器指令
    MRC Pn,op1,Rd,CRn,CRm,op2 //mrc p15,0,r0,c1,c0,0 读入cp15的c1寄存器的内容到r0中
    MCR Pn,op1,Rd,CRn,CRm,op2 //mcr p15,0,r0,c1,c0,0 写r0内容到cp15的c1寄存器中
    @ set Vector Base Address 为0x40008000
    ldr r0,=0x40008000
    在这里插入图片描述

波形控制

I2C设备

双线 i2c (半双工 同步)
支持一主机对多从机
可主从切换
在这里插入图片描述
在这里插入图片描述

PWM蜂鸣器

  • PWM(Pulse Width Modulation) :
    脉冲宽度调制 。常见应用有:电机控制,DAC输出等
  • 占空比:
    就是输出的PWM中,高电平保持的时间 与该PWM的时钟周期的时间之比 在这里插入图片描述

    WDT看门狗

    工作原理:在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位 在这里插入图片描述

    PWM控制框图 在这里插入图片描述 在这里插入图片描述

/*PWM控制蜂鸣器*/
 void PWM_init(void)
{
	GPD0.CON = (GPD0.CON & ~(0xf))    | 0x2;    // GPD0_0 : TOUT_0
	PWM.TCFG0 = (PWM.TCFG0 & ~(0xFF)) |0x63; //Prescaler 0 value for timer 0; 99 + 1 = 100
	PWM.TCFG1 = (PWM.TCFG1 & ~(0xF))  | 0x3;  // 1/8 input for PWM timer 0
		
	PWM.TCNTB0 = 200;   //设置脉冲周期数 200    TOUT PWM输出频率 = TLK/周期数
	PWM.TCMPB0 = 100;   //设置占空比   TCMPB0/TCNTB0 =100/200=1/2  
		
	/* auto-reload, Inverter Off, manual update */
	PWM.TCON = (PWM.TCON & ~(0XF)) | 0XA;
	/* auto-reload, Inverter Off, manual update off, start Timer0*/
	PWM.TCON = (PWM.TCON & ~(0xF)) | 0X9;	
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值