FLASH相当于电脑的硬盘,内存相当于电脑的运行内存,控制器搬移这些运算指令,(ALU)运算机进行解析,寄存器用于临时存放用于运算的数据
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
搬移指令
@ 实现 延时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
堆栈操作指令
异常与工作模式关系:
复位,软中断 -->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
@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开发环境搭建
串行通信与并行通信的区别:串行通信只是用一根信号线传输数据,每次传输一位,串行传输的特点是线路简单成本低。而并行通信是使用多根信号先进行信号传输,相当而言并行传输更开快。
同步与异步的区别:接受与发送使用的是否是同一时钟
/*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) }
}
双线 i2c (半双工 同步)
支持一主机对多从机
可主从切换
/*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;
}