【ARM编程模型】
硬件: 电路原理图 软件: 体系结构, 指令集, 寄存器组
【ARM编程技术】
汇编/C语言 编译, 链接, 烧写和调试 windows: MDK linux : gcc
【ARM接口编程】
电路原理图 datasheet ------> 裸机程序(不带操作系统,直接操作硬件) 中断技术 初始化程序
【 ARM基础知识】
1. 冯·诺依曼结构特点: 采用二进制表示数据和程序 事先存储程序 利用控制流来驱动程序 五大部件 2. CPU = CU + ALU CPU: 解释并执行指令的功能部件 CU: 译码并发出各种控制信号 ALU: 运算
3. 常见存储器 RAM: (掉电丢失数据) SRAM: 静态 96KB DRAM: 动态(SDRAM, DDRII(256MB))
ROM: (掉电不丢失数据) PROM EPROM E2PROM BIOS
flash: norflash : 2MB 有地址线, 片上执行(CPU可以直接访问), 读数据速度快, 存储启动程序 nandflash: 256MB 无地址线, 不片上执行, 存储大数据量数据
【ARM体系结构】
1. ARM : advanced RISC machine 含义: 公司 技术 微处理器
2.编程模型 1)数据和指令类型: ARM处理器支持: 8bit, 16bit, 32bit数据 Thumb指令集, ARM指令集 半字对齐: 被2整除 [0]=0 字对齐 : 被4整除 [1:0]=00
例子: 运行地址 机器码 机器码符号化 0x00000000 E3A00001 MOV R0,#0x00000001
2)处理器工作模式 ARM处理器一般支持 7 种基本工作模式 不同的工作模式对应着不同的寄存器 特殊寄存器: R13(SP), R14(LR), R15(PC), CPSR, SPSR
程序指针PC (r15): 程序执行到哪里(地址), PC指针指到哪里(地址)
3. 异常处理过程 产生:(硬件过程) 1. CPSR ---> SPSR_mode 2. 设置CPSR CPSR[5] = 0 处理器处于ARM状态 CPSR[4:0]=mode 异常工作模式 CPSR[7:6]=11 根据实际情况
3. 返回地址--->LR_mode 4. PC ---> 异常向量表入口地址 5. 保护现场(堆栈)
退出:(软件过程) 1. SPSR_mode ---> CPSR 2. LR_mode----> PC 3. 恢复现场(堆栈)注意: 异常产生,退出时,ARM处理器必须处于ARM工作状态
【指令集】
1. RISC: 精简指令集系统 特点: 机器码是固定长度 指令规整简单 单周期执行, 容易流水线机制 大量寄存器 访问存储设备: 加载,存储, 交换
2. ARM指令格式 操作码 + 目标寄存器 + OP1 + OP2.... |---助记符 |---条件码
3. 第二操作数寻址方式 立即数寻址: mov r1, #1 寄存器寻址: mov r0, r1 移位寻址 : mov r0, r1, lsl #2 寄存器间接: ldr r0, [r1] 基址寻址 : ldr r0, [r1, #2] 多寄存器 : ldmia r0!, {r0-r7} 堆栈寻址 : stmfd sp!, {r0-r12, lr} 相对寻址 : b label
【系统设计】
1. SOC概念: system on chip ARM芯片 = ARM内核 + 片内存储器 + 片内各种电路 S5PC100 cortex-A8 SRAM... 各种控制器
2. 指令流水线 在编译器中, 黄色箭头指向的地址 = 执行PC = R15(PC)
ARM态: PC值 = 当前执行指令PC + 8 (取指PC)
Thumb态: PC值 = 当前执行指令PC + 4 (取指PC) 例如: add pc, pc, #4 ; PC = PC + 4 ; R15 = 取指PC + 4 3. ARM920T = ARM9TDMI + I/D cache + MMU + cp14 + cp15
【ARM指令集】
1. 条件执行及标志位 cmp: 功能相当于减法运算,不保存结果, 但影响标志位 条件码: 16条, AL
2. 分支指令 B : 相当于C语言goto label, 不带返回地址 BL: 相当于C语言函数调用, 处理器自动保存下一条指令的返回地址 ---> LR
跳转范围 = 偏移量(机器码低24位) * 4 + 当前PC值(取指PC) = 0x1 * 4 + (0x8+8) = 0x14
例子: 0x00000008 EB000001 BL 0x00000014长跳转: ldr pc, =label
注意: B,BL指令是位置无关指令
3. 数据处理指令 注意: 上述指令只能对寄存器操作,不能针对存储器ADD SUB AND EOR ORR CMN CMP TST TEQ BIC
4. 桶型移位器 逻辑移位: lsl lsr 算术移位: asr 循环移位: ror 左移 = 32 - 右移
5. 立即数 第二个操作数有12位: 低8位: 常数 高4位: 移位次数---> 循环右移 * 2
如何判断常数是否是合法立即数? 1. 利用编译器 例如: 0x00000004 E3A014FF MOV R1,#0xFF000000 低8位: 0xff 高4位: 4 * 2 = 8(循环右移8位)
2. 规则 1. 找出8位常数 第一个"1"和最后一个"1", 最短距离不能超出8位 2. 常数能否循环右移偶数位 6. 装载32 bit常数 伪指令: 不会生成一一对应的机器码ldr r0, =const const: 符合立即数 mov r0, #const const: 不符合立即数 ldr r0, [pc, #offset]
7. 单寄存器数据传送 ldr: 读 源寄存器在后, 目标寄存器在前 str: 写 源寄存器在前, 目标寄存器在后
前索引: LDR r0,[r1,#8] 后索引: LDR, R0,[R1],#8
8. 块数据传送 ldm: 读 源寄存器在后, 目标寄存器在前 stm: 写 源寄存器在后, 目标寄存器在前
操作多寄存器访问: 1. 低编号的寄存器对应着低地址数据 2. 读取存储设备数据分别存放在寄存器参数列表中 3. 指针向上/下移动
9. 堆栈
1)堆栈指针移动方向: 向上: 写入数据(压栈), 指针递增 向下: 读取数据(出栈), 指针递减 2) 满堆栈: 堆栈指针指向最后入栈的有效数据的位置 空堆栈: 堆栈指针指向等待下一个入栈的数据的空位置
STMFD (压栈) LDMFD (出栈)
注意: 运行C语言main函数之前,必须确保堆栈空间已经设置好了!!!
异常处理退出过程: 软件过程中 1. SPSR--->CPSR 2. LR --->PC 3. 恢复现场
例如: 保护现场: stmfd sp!, {r0-r12, lr} 恢复现场: ldmfd sp!, {r0-r12, pc}^ 解释: 堆栈列表中, 如果有"PC"和"^"同时存在, 表示处理器自动将SPSR_mode ---> CPSR, LR_mode--->PC
10. SWP软交换 作用: 原子操作, 不可被外部因素打断. 唯一一条原子操作的访存指令
11. 软件中断 (SWI) 格式: swi 软中断号
swi是一种用户自定义指令,当执行到swi指令时, 跳转到异常向量表0x8入口地址, 自动切换到SVC工作模式
12. PSR 传送指令 msr/mrs 设置CPSR/SPSR指令
【异常处理过程】
1、产生:(硬件过程) 1. CPSR ---> SPSR_mode 2. 设置CPSR CPSR[5] = 0 处理器处于ARM状态 CPSR[4:0]=mode 异常工作模式 CPSR[7:6]=11 根据实际情况
3. 返回地址--->LR_mode 4. PC ---> 异常向量表入口地址 5. 保护现场(堆栈)
2、退出:(软件过程) 1. SPSR_mode ---> CPSR 2. LR_mode----> PC 3. 恢复现场(堆栈)注意: 异常产生,退出时,ARM处理器必须处于ARM工作状态
【异常返回地址】
前提: 在异常产生的时候内核设置 LR_mode = PC - 4
【搭建linux下ARM开发环境】
1. 将arm-none-eabi-4.2.2.tgz拷贝到ubuntu /usr/local目录,然后解压 2. 将解压之后生成arm目录,改变权限 3. 将交叉开发工具链的路径: /usr/local/arm/4.2.2-eabi/usr/bin 添加到 /etc/environment 4. 更新工具链路径 source /etc/environment
arm_project工程有: start.S uart.c serial.c 1. 将源文件编译生成 *.o文件 gcc 2. 将*.o文件链接生成*.elf文件 ld 3. 将*.elf文件转换生成*.bin文件 objcopy 4. 将*.elf文件反汇编生成*.dis文件 objdump
【系统时钟】
1. 各种不同的外围设置需要的工作频率: CPU: 667MHZ 内存: 200MHZ 串口: 115200bps ... ... 2. 设置不同的外围设备需要的工作频率: 如何设置不同的工作频率? 12MHZ ---> 667MHZ工作频率 倍频 最高工作频率---> 各种不同的设备 分频 PLL锁相环
3. 查看用户手册 S5PC100通过3个域管理不同外围设备需要的工作频率: D0: ARMCLK, HCLK, PCLK D1: HCLK, PCLK D2: HCLK, PCLK
4. PLL工作原理 1. 设置PLL使能信号 2. 设置PLL LOCKTIME 3. 设置Fin和Fout之间参数 4. 设置比例关系: D0: ARMCLK:HCLK:PCLK = 1:4:8 D1: MPLLCLK:PCLK = 1:4
5. 系统时钟寄存器 1. A/M/E/HPLL_MASK: LOCKTIME = 0xe10 2. A/M/E/HPLL_CON : PLL使能信号, 设置输出工作频率的参数 3. CLK_SRC0~3 : 选择时钟源, CLK_SRC0 = 0x1111 4. CLK_DIV0~4 : 分频比例关系
6. 编写驱动系统时钟流程: 1. 设置LOCKTIME 2. 设置分频比例关系 3. 使能PLL信号,设置输出工作频率的参数 FOUT = MDIV X FIN / (PDIV X 2^SDIV) 4. 选择时钟源为输出
【中断控制器】
中断处理过程流程: 1. 建立异常向量表 2. 关闭看门狗, 初始化系统时钟 3. 设置堆栈空间: IRQ_stack system_stack 4. 开CPSR[I]=0 5. 跳转C语言main函数 6. 正常程序 7. 中断初始化 1) 设置K1(GPH0_1/EINT1)引脚功能为: 中断输入 GPH0CON[7:4] = 0010 WKUP_INT[1] 2) 设置K1(GPH0_1/EINT1)触发方式为: 下降沿触发 WKUP_INT0_7_CON[6:4]=010 Falling edge triggered 3) WKUP_INT0_7_MASK &= ~(0x1<<1); VIC0INTENABLE |= (0x1<<1); VIC0VECTADDR1 = (unsigned long)int_key1; //ISR函数入口地址
8. 产生IRQ信号: 保护现场 调用ISR: ((void (*)(void))VIC0ADDRESS)(); //强制将VIC0ADDRESS转换成函数指针 LED亮 清中断: VIC0ADDRESS = 0; WKUP_INT0_7_PEND = (0x1<<1); 恢复现场 调整返回地址
【串口控制器】
1. 使用uart需要设置: 波特率 数据位 奇偶校验位 停止位 流控2. uart寄存器 ULCONn: 设置uart传输格式 8N1
UCONn : 选择时钟源, 传输方式 UTRSTATn: 检测uart发送/接收状态 UTXHn/URXHn:发送/接收数据 UBRDIVn/UDIVSLOTn: 波特率 DIV_VAL = (PCLK / (bps x 16 ) ) −1 = (66.75mhz/(115200*16)) - 1 = 35.2UBRDIVn = 35
UDIVSLOT0 = 33. 编写uart 程序流程: 1. 设置GPA0_0,1引脚功能: GPA0CON[3:0] = 0010 UART_0_RXD GPA0CON[7:4] = 0010 UART_0_TXD
2. 设置系统时钟 3. 初始化uart: 波特率 数据位 奇偶校验位 停止位 流控4、串口出现乱码原因 硬件: 串口线型号
软件: 系统时钟,波特率,串口程序【PWM timer】
1. PWM timer操作原理 参考网络解释2. timer寄存器 TCFG0: 一级预分频值
TCFG1: 二级5路分频值 Timer Input Clock Frequency = PCLK / ( {prescaler value + 1} ) / {divider value} = 66.75MHZ / 250 / 8 = 33.375KHZ = 0.03msTCON: 启动/停止, 装载方式
TCNTBn: 计数值 TCMPBn: 比较(控制高电平)3. 编写驱动PWM timer程序流程: 1. 设置GPD1/pwmtout1引脚功能: GPDCON[7:4] = 0010 TOUT_1
2. 初始化系统时钟 3. 初始化pwm timer1: 设置一级预分频值 设置二级5路分频值 设置计数值 设置比较值 启动timer1(手动装载, 自动)
【nand控制器】
查看用户手册 查看nandflash芯片手册 查看电路原理图 结合分析 编写程序1.nandflash内部结构 1个nand器件 = 2048块
1块 = 64页 1页 = (2048 + 64)B 2048B: 代表main区,存放正常数据 64B: 代表spare区(OOB区), 存放EDC/ECC校验码读写nandflash以 页 为单位
擦出nandflash以 块 为单位2. nand容量 256MB:
二进制 28bit 表示 寻址: 发 5 次地址3. nand寻址 以 页 为单位
A0~A11 : 列地址(页内地址) A12~A28: 行地址(页地址)4. nandflash操作步骤 发命令: 复位: 0xff 读: 0x00 0x30
发地址: 连续发5次地址(发2次列地址, 发3次行地址) 检测nandflash: 读写: 5. nand寄存器 NFCONF: 选择时钟, 设置时序时间 NFCONT: 使能nand控制器, 片选nandflash芯片 NFCMMD: 命令 NFADDR: 地址 NFDATA: 数据 NFSTAT: 检测nandflash状态 6. 编写nandflash驱动程序流程 1. nand初始化 1.1 设置GPK0_2,3引脚功能: GPK0CON[11:8]=0011 NFCSn[0] GPK0CON[15:12]=0011 NFCSn[1] 1.2 选择时钟源, 设置时序时间 1.3 使能nand控制器 1.4 nandflash复位: 片选nandflash芯片 发出复位命令: 0xff 检测nand状态 取消nandflash片选2. 从nandflash拷贝数据到内存中
2.1 定义目标地址ddr_start, 源地址nand_start, 数据大小len 2.2 片选nandflash芯片 2.3 循环读取数据到内存中 for(i=nand_start; i<(nand_start+len); i+=2048) //页地址 { 发地址准备命令: 0x00 连续发5次地址(发2次列地址, 发3次行地址) 发数据准备命令: 0x30 检测nand状态for(j=0;j<2048;j++) //页内地址
{ } } 3. 取消nandflash片选
【A/D转换器】
1. A/D概念: 将电信号(模拟信号)转换为数字量(数字信号,二进制)2. A/D转换原理 采样---> 量化 ----> 编码
3. A/D编码位数: 12位 Vmax = 3.3V
Vmin = 0 V4. A/D转换公式 采样值 = (4096/3.3V) * 采样电压 (4096 = 2 ^ 12,12个输出)
5. ADC操作方法: 选择输入通道: 采样通道[5:0] 触摸通道[9:6]
ADC初始化6. ADC寄存器 ADCCON: 设置输出编码位数, 检测adc转换状态, 设置预分频值, 启动读
ADCDAT0: 采样值 ADCMUX: 输入通道选择 ADCMUX = 0000 AIN07. 编写ADC驱动程序流程: 1. 设置系统时钟
2. 初始化ADC 选择输入通道 设置输出编码位数, 检测adc转换状态, 设置预分频值, 启动读 读取采样值
【I2C控制器】
1. I2C寄存器 I2CCONn: 使能ACK信号, 选择时钟源, 使能传输方式 I2CSTATn: 设置主机发送/接收模式, 启动/停止, 使能输出, ACK信号能否被接收 I2CDSn: 发送/接收数据
2. 编写I2C驱动程序流程:(根据LM75时序图) 设置GPD3,4引脚功能: GPDCON[15:12] = 0010 I2C0_SDA GPDCON[19:16] = 0010 I2C0_SCL
第一阶段: 1. 装载LM75地址: 0x90
2. 设置主机发送模式, 使能ACK信号能被接收, 使能数据输出, 设置开始条件 3. 使能ACK信号, 选择时钟源, 使能传输方式 4. 等待5. 配置LM75模式: 温度模式 0x0
6. 使能ACK信号, 选择时钟源, 使能传输方式第二阶段: 1. 装载LM75地址: 0x90
2. 设置主机接收模式, 使能ACK信号能被接收, 使能数据输出, 设置开始条件 3. 使能ACK信号, 选择时钟源, 使能传输方式 4. 等待5. 使能ACK信号能被接收, 接收第一个字节数据(温度整数部分)
6. 关闭ACK信号被接收 , 接收第二个字节数据(温度小数部分) 7. 设置停止条件
@成鹏致远 | 2013-08-30