【归纳】S3C2440A之ARM学习的所有的问题:

目录:

问题1:关于Nor/Nand启动判断的问题:
问题2:程序烧写到什么位置啦(即程序的存储位置)?Norflash/Nandflash?
问题3:Nor启动,SRAM除了存储寄存器sp之类,还有别的作用吗?
问题4:ARM中Makefile的规则、语法、函数及实例
问题5:变量/函数的声明、定义、初始化的作用和区别?
问题6:UART特殊功能模块的寄存器操作机理和使用疑难的问题
问题7:操作寄存器的规范
问题8:ARM架构芯片(如ARM9-s3c2440a),寄存器都是32位,为什么?
问题9:S3C2440芯片手册中,EINT[]; 什么意思?
问题10:编译时,反汇编 *.dis 文件显示,0X0地址非<_start>,编译生成的 *.bin 文件在开发板显示失败的原因查找
问题11:关于头文件包含的问题
问题12:关于使能内部上拉的问题
问题13:关于波特率分频寄存器UBRDIVn,公式为什么这样写的问题
问题14:查看赋值操作和位操作时的寄存器的类别情况(
UART0回显程序完成并运行成功之后查看

问题15:怎样提高程序查错调试的效率?

正文:

问题1:关于Nor/Nand启动判断的问题:

视频讲解是用写0到[0x0],取出还是0则Nand启动,视频例程:
mov r1, 0
ldr r0, [r1]
str r1, [r1]
ldr r2, [r1]
cmp r1, r2
//如果r1 != r2, nor启动
ldr sp, =0x40000000 + 4096
//如果r1 == r2, nand启动
moveq sp, #4096
streq r0, [r1]
如果
拨码开关为Nor启动,Norflash[0x0]地址中数据原本
即为0,Nor启动不能写入,但cmp r1, r2时,r1==r2依然

成立,系统是否会判断为Nand启动?有没有影响?怎么判断?

答:1>【传说】hceng(1307410945):不会为0的,因为你的代码烧在nor/nand 0地址上,既然有代码,就不会为0;    

hexdump xx.bin 你会看到 首个数据 不会是0的;  

 正因为 0地址的数据是有用的,所以才会先保存到r0,最后再从r0恢复: ldr r0, [r1]   ->   streq r0, [r1]

nand启动才会修改,nor不会。 也就是说 nor启动,下次还能启动,,先nand启动就废了

你试试 去掉streq r0, [r1]  理论上 下次就启动不了,,我没试,我是从理论上分析的。。。帮忙试下 告诉结果。。。==
 
2>会 可以改下代码先判断nor中第一个是否和写入的相同
3>这个跟判断 大小段端  一个原理  差异处 区分 

问题2:裸板实验操作流程:Windows环境下编写start.S、*.c、Makefile文件,文件上传到Ubuntu环境(即主机)并编译连接生成*.bin文件,把.bin文件通过DOS系统的命令行工具烧写到开发板Norflash/Nandflash,把开发板启动开关调至相应的启动开关,关闭电源拔掉eop烧写器,重新上电,观察开发板现象。
程序烧写到什么位置啦(即程序的存储位置)?Norflash/Nandflash?
芯片有多种启动方式,而这些启动方式都可以由配置引脚来选择。芯片在启动时读取这些配置引脚的电平,就可以判断从那种方式启动。通常在研发阶段的实验板或者很多学习板(如:JZ2440)通常采用开关或跳线帽等方式来将配置引脚的电平置为高电平或置为低电平。在出厂时, 配置引脚一般通过上下拉电阻来设置电平。

从NOR FLASH启动:

    从NOR FLASH启动时,由前面的图1,由nGCS0控制的bank0直接连接了nor flash,而bank0能访问的地址范围为:256M(0X00000000----0X08000000),JZ2440开发板使用的nor flash大小为2M(0X00000000----0X00200000),从而S3C2440芯片的物理地址(0X00000000----0X00200000)就由nor flash来占据。

    选择从NOR FLASH启动,上电,S3C2440芯片就会去运行nor flash上地址为0x0处的指令。从后面的实验中,可以清楚的知道,读nor flash可以像读内存那样读,但是要用额外的命令向nor flash写入数据。如果nor flash像内存那样读和那样写,那nor flash完全可以被内存所替代。

(摘录自:博客园-韦东山  https://www.cnblogs.com/weidongshan/p/6689728.html)
由此可知,(1)DOS环境下命令行工具烧写设置决定了.bin文件的烧写到开发板的存储位置(即Norflash、Nandflash),因此,不管是Nor/Nand启动,地址[0x0]一定不为空(即地址[0x0]内的数据一定不为0);
(2)S3C2440的启动方式则由OM0和OM1引脚(开发板具体通过拨码开关控制)来选择因此,当程序烧写到Norflash而选择Nandflash启动开发板不会有现象,Nandflash同理。
(3)执行程序时,通过在程序中对已知启动方式的判断,来设置栈区地址(个人理解程序开头对启动方式进行判断的作用就是干这个的)。程序执行是从0x0地址开始:(假设Nand启动)Nandflash中已经存有代码,地址[0]数据不为0,因此,通过【写0到[0x0],然后取出与0比较:如果r1 != r2, nor启动;如果r1 == r2, nand启动;假设r1 == r2】则表示程序存储在Nandflash(以地址0x0开始)且前4k映射到SRAM,栈区在SRAM顶部,地址为4096;    (假设Nor启动)Norflash中已经存有代码,地址[0]的存储空间不可进行写操作且数据不为0。Nor启动,则程序直接在Norflash启动,栈区地址也在SRAM顶部,地址为(0x40000000+4096)。因此,杜绝了Nand/Nor启动时地址[0x0]的空间未被赋值0时原本数据为0的可能性。
(此即问题1的答案)

问题3:Nor启动,SRAM除了存储寄存器sp之类,还有别的作用吗?

问题4:ARM中Makefile的规则、语法、函数及实例

1.Makefile的说明:

1)功能【gcc -Werror -Iinclude  -c  -o  c.o  c.c  -MD  -MF   .c.o.d】来自gcc,和Makefile本身无关

2)当工程中文件(包括 .c/ .h/ .s ...)路径发生变化后,需要【make distclean】一次,再重新make(为什么?)

3)当工程文件(包括 .c/ .h/ .s ...)只执行了内容修改,直接make即可重新编译生成  .o文件,原来的依赖可直接用(?)

2. arm-led工程:include/s3c2440_soc.h、led.c、Makefile、start.S四个文件

实现功能:单灯流水、双灯流水、三灯流水,循环

实验结果:完全实现预定功能,可参考成功arm-Makefile例程如下:


问题5:变量/函数的声明、定义、初始化的作用和区别?

(1)变量:声明是告诉编译器有这么个变量,但并不实现。定义就是实现这个变量,真正在内存(堆或栈中)为此变量分配空间

声明简单的说就是 定义变量类型但不为其分配空间。


定义就是指定变量类型,然后分配空间并为其初始化。

(2)函数:声明嘛表示有这么个函数了,定义就是具体实现了,举个例子:
函数声明:
int fun(int a, int b);    //a,b为形参

函数定义:
int fun(int a,int b)
{ int c;
c=a+b;

return c; }

函数调用(即函数初始化):

fun(3, 5);        //3, 5为实参



声明就象是定义的头部,比较简略,函数是用来调用的,如果函数定义部写到调用的位置后面,执行到调用位置,后面根本没执行就找不到了,当然报错了,就要在前面加声明,表示有这个函数,反之先写函数体,后调用声明就用不了,不在同一源文件,道理也是如此

//声明在函数外,声明后面的函数(无论main还是其他函数)都可以调用                //声明在函数内部,只能在本函数内,声明后面的区域可以调用    
问题6:UART特殊功能模块的寄存器操作机理和使用疑难的问题
    6.1 配置UART的几个寄存器:

UBRDIVn寄存器:设置波特率,S3C2440 UART的时钟源有两种选择:PCLK、UEXTCLK、FCLK/n,其中n的值通过UCON0-UCON2联合设置

ULCONn寄存器:设置传输格式

UCONn寄存器:它用于选择UART时钟源、设置UART中断方式

UFCONn寄存器、UFSTATn寄存器,UFCONn寄存器:用于设置是否使用FIFO,设置各FIFO的触发阙值,即发送FIFO中有多少个数据时产生中断、接收

      FIFO中有多少个数据时产生中断。并可以通过设置UFCONn寄存器来复位各个FIFO。读取UFSTATn寄存器可以知道各个FIFO是否已经满,其中有多少个数据。

UMCONn寄存器、UMSTATn寄存器:这两类寄存器用于流量控制,具体看数据手册UTRSTATn寄存器:它用来表明数据是否已经发送完毕、是否已经接收到数据

UERSTATn寄存器:用来表示各种错误是否发生

UTXHn寄存器:CPU将数据写入这个寄存器,UART即会将它保存到缓冲区中,并自动发送出去

URXHn寄存器:当UART接收到数据时,CPU读取这个寄存器,即可获得数据。

    6.2
问一个关于串口的问题:
int puts(const char *s)
{
	while(*s)
	{
		putchar(*s);
		s++;
	}
}
当字符串结束,*s=0,跳出while()循环
问:为什么字符串结束后, *s一定等于0?
答:对于字符串,程序是当做字符数组处理的,在数组的结尾,程序会自动加一位元素'\0'作为结束标志,因此,*s = ‘\0’,即为空,为假,跳出循环

    6.3 UART0作调试打印功能时,输入之后再PC机串口工具回显输出乱码的问题

答:可能的原因有  种,

    01:波特率设置不对,导致此的可能原因有:

          a)UBRDIV0设置或计算错误;

            b)UCON0  位[11 : 10]时钟选择错误:

            UCON0  位[11 : 10],选择 PCLK,UEXTCLK 或 FCLK/n 给 UART 波特率。

            UBRDIVn = (int)(被选时钟 / (波特率 × 16) ) – 1;              00 = PCLK  10 = PCLK  01 = UEXTCLK  11 = FCLK/n  

            (如果希望选择 FCLK/n,应该在选择或取消选择 FCLK/n 后加上“NOTE”的代码。)

void uart0_init(void)
{
	//设置UART0输出输入引脚
	GPHCON &= ~((3<<4)|(3<<6));
	GPHCON |= ((2<<4)|(2<<6));
	//GPHUP &= ~((1<<2)|(1<<3));    //该句屏蔽不影响串口的操作,为什么要写?操作GPF4,5,6时问什么不需要设置?
	//设置波特率
	UCON0 = 0x00000005;    //若UCON0 = 0x00000003,则PC机串口工具无输出显示
	UBRDIV0 = 26;
	//设置数据格式
	ULCON0 = 0x00000003;	//8n1
	
}

        c)时钟分频寄存器CLKDIVN = 0b101 = 5,设置错误

/*设置时钟分频寄存器CLKDIVN = 0b101 = 5*/
	ldr r0, = 0x4c000014
	mov r1, #5        //若#5改为 #3 ,则PC机串口工具输出乱码
	str r1, [r0]
	/*如果 HDIVN 不为 0,CPU 总线模式应该使用以下指令使其从快总线模式改变为异步总线模式(S3C2440
	*不支持同步总线模式)
	*/
	MRC  p15, 0,  r0,  c1,  c0,  0
	ORR  r0,  r0,  #0xc0000000
	MCR  p15, 0,  r0,  c1,  c0,  0
	/*设置多层锁相环控制寄存器MPLL,使得MPLL = 400MHz*/
	ldr r0, = 0x4c000004
	ldr r1, = ((92<<12) | (1<<4) | (1<<0))
	str r1, [r0]
     02:数据格式不对?
问题7:操作寄存器的规范
改变寄存器的话。读 改 写 三步。别影响其他位。
对某个寄存器进行操作时,先对该寄存器清零,再赋值。

详细:见【归纳】C语言代码编写规范——ARM


问题8:ARM架构芯片(如ARM9-s3c2440a),寄存器都是32位,为什么?

答:寄存器的本质是内存,且ARM架构下固定的32 bits 操作码(opcode)长度,降低编码数量所产生的耗费,减轻解码和流水线化的负担。大多均为一个CPU周期执行。组成操作码字段的位数一般取决于计算机指令系统的规模。

问题9:S3C2440芯片手册中,EINT[]; 什么意思?

_EINT();是打开全局中断,跟51中EA=1;效果相似
_DINT();是关闭全局中断,跟51中EA=0;效果相似
_EINT();是打开全局中断,跟51中 EA =1;效果相似
_DINT();是关闭全局中断,跟51中 EA =0;效果相似

问题10:编译时,反汇编 *.dis 文件显示,0X0地址非<_start>,编译生成的 *.bin 文件在开发板显示失败的原因查找

Makefile:
arm-linux-ld -Ttext 0 start.o led.o uart.o init.o main.o -o uart.elf
该步完成链接功能,把汇编好的OBJ(机器码)文件、库文件链接起来,最终形成可以在特定平台运行的可执行文件,用到的工具为arm-linux-gcc。
链接顺序从  arm-linux-ld -Ttext 0  之后,从0X0地址开始,依次存放start.o led.o uart.o init.o main.o文件,顺序一定要注意,start文件在第一位,main文件在最后位

具体原理不是很清楚,不过大概是链接器从左到右依次解析输入文件,判断是否是目标文件与存档文件

问题11:关于头文件包含的问题

11.1 对头文件的包含的问题:
1)使用文件和头文件在同一个目录,用法:#include "s3c2440_soc.h"
2)使用文件和头文件不在同一个目录,用法:#include <s3c2440_soc.h>

需要注意的是,方法2有时候对不再同一个目录的头文件包含时会出错,原因未知

    例如:下面main()中使用头文件:

    1.#include <s3c2440_soc.h>    #include <uart.h>

    Makefile1可以顺利编译,Makefile2不可

现有工程文件E:\Linux-ARM编程\一期ARM裸板程序\内存控制器\NorFlash
start.S, Makefile, main.c, led.c  文件夹:include 
头文件夹include:s3c2440_soc.h, uart.h

Makefile:
objs = main.o  led.o  start.o
dep_files := $(patsubst %, .%.d, $(objs))
dep_files := $(wildcard $(dep_files))
CFLAGS = -Werror  -Iinclude
all: $(objs)
	arm-linux-ld -Ttext 0 start.o  led.o  main.o -o led.elf
	arm-linux-objcopy -O binary -S led.elf led.bin
	arm-linux-objdump -D led.elf > led.dis
ifneq ($(dep_files), )
include $(dep_files)
endif
%.o: %.c
	arm-linux-gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d
%.o: %.S
	arm-linux-gcc $(CFLAGS) -c -o $@ $< 
clean:
	rm  *.o  *.dis   *.elf  *.bin
distclean:
	rm $(dep_files)
.PHONY:
	clean

Makefile2:
all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o uart.o uart.c
	arm-linux-gcc -c -o init.o init.c
	arm-linux-gcc -c -o main.o main.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o led.o uart.o init.o main.o -o uart.elf
	arm-linux-objcopy -O binary -S uart.elf uart.bin
	arm-linux-objdump -D uart.elf > uart.dis
clean:
	rm *.bin *.o *.elf *.dis


11.2 头文件的制作

例程:

#ifndef		_MY_PRINTF_H
#define		_MY_PRINTF_H

int printf(const char * fmt, ...);
int my_printf_test(void);

#endif

参考链接

http://topic.csdn.net/u/20101125/22/15af528d-d417-4f6b-8fa9-565e3d980eb2.html
http://blog.csdn.net/zlwzlwzlw/archive/2009/12/07/4955560.aspx

http://hi.baidu.com/paladin1893/blog/item/8dab19510325586b843524e1.html

https://blog.csdn.net/duan19920101/article/details/50991794

问题12:关于特殊功能寄存器使能内部上拉的问题


控制LED灯的寄存器GPF4,5,6使能内部上拉没有专门写到程序,而控制UART0的输出输入引脚的GPH2,3程序专门写了使能内部上拉

已知:芯片重启时,GP_UP端初始状态[0x00],0:默认使能附加上拉功能到相应端口引脚

问1:UART0的输出输入引脚的GPH2,3为什么还写内部上拉?为了程序严谨吗?

问2:控制LED灯的寄存器GPF4,5,6使能内部上拉为什么不写?不担心内部电平过低(当GPF4,5,6的使能端被改动时)?

问题13:关于波特率分频寄存器UBRDIVn,设置公式为什么这样写的问题

UBRDIVn: UART baud rate divisor registers  → UARTUART  波特率分频寄存器

baud英 [bɔːd] 美 [bɔd]    n. 波特(信息传输速率的单位)       n. (Baud)人名;(英)鲍德;(法)博

rate英 [reɪt] 美 [ret]    n. 比率,率;速度;价格;等级      vt. 认为;估价;责骂    vi. 责骂;被评价

divisor英 [dɪ'vaɪzə]美 [dɪ'vaɪzɚ]n. 除数;因子

波特率发生

每个UART 的波特率发生器为发送器和接受器提供串行时钟。波特率发生器的源时钟可以选择 S3C2440A 的内部系统时钟或 UEXTCLK。换句话说,分频由设置 UCONn 的时钟选项选择。波特率时钟是通过 16 和由 UART波特率分频寄存器(UBRDIVn)指定的 16 位分频系数来分频源时钟(PCLK,FCLK/n 或 UEXTCLK)产生的。
    UBRDIVn 由下列表达式决定:
    UBRDIVn = (int)( UART 时钟 / ( 波特率 × 16) ) - 1
    (UART 时钟:PCLK,FCLK/n 或 UEXTCLK)
当然,UBRDIVn 应该是从 1 至(2 16 -1),只有在使用小于 PCLK 的 UEXTCLK 时设置为 0(旁路模式)。
例如,如果波特率为 115200 bps 并且 UART 时钟为 40 MHz,则 UBRDIVn 为:
    UBRDIVn = (int)(40000000 / (115200 x 16) ) - 1
    = (int)(21.7) - 1 [ 取最接近的整数]

    = 22 - 1 = 21

问题14:UART0回显程序完成并运行成功之后,查看赋值操作和位操作时的寄存器的类别情况

答:1)对寄存器的赋值运算只是简单的赋值单项运算; 对寄存器的位操作大多是把数值进行位操作之后与寄存器进行逻辑运算,然后把结果再赋值给寄存器,算是复合运算
2)进行位操作复合运算的全部是I/O Ports寄存器,而进行单项赋值运算的则全是特殊功能寄存器

问题15:怎样提高程序查错调试的效率?

1)有么有可以查看一段内存中所有数据的办法?并且可以查看这段内存的代码和地址?

原因:当程序出错时,编译器播出错误的地方,但实际错误可能只在最前面,这样检查错误耗费大量时间,于是想直接查看程序在开发板上的地址和对应的代码,这个方法现实吗?有没有呢?貌似反汇编可以完成这个任务?但汇编太不人性化,很难

解决方法:

2)按照编译器提示的错误原因和错误代码位置,细心逐行排除

例:JZ2440一个按键控制LED的程序,编译链接时出现:

问题发现:指令后面的 ';'未加,很简单,费了好久时间才找到原因,逐行排除法最快


问题16:关于全局变量g_char、g_char3进行自加运算时,一直输出同样的大小写字母的解决
==============================================================
SDRAM设置之后,测试结果:
--------------------------------------------------------------
当g_char、g_char3是main()函数的局部变量时:
Nand运行时:AaBbCcDd ...  LED循环
Nor运行时 :AaBbCcDdEeFfGgHhIiJjKkLlMmNn ...  LED循环
--------------------------------------------------------------
当g_char、g_char3是main()函数之前的全局变量时:
Nand运行时:AaBbCcDdEeFfGgHhIiJj ...  LED循环
Nor运行时 :AaAaAaAa ...  LED循环
==============================================================
加入链接脚本后:
Nand运行时:%%%%%%%% ...  LED循环
Nor运行时 :%%%%%%%% ...  LED循环
=============================================================
问题查找:
在于main()函数中循环判断while():
1)while(1)打印:ABCDE...
2)while(sdram_test() == 1)时,持续输出一个不变字符达不到预期目的, 如:UUUU...
解答:
在判断或执行sdram_test();函数后,[0x30000000]及之后的100000个字节的内存被该函数赋值重新装数;
当只有一个字符自加结果打印时,只会输出被修改后的数,而主程序要实现字符自加的目的将永远不能实现
当有多个字符自加结果打印时,每次打印两个被修改的数,而主程序要实现字符自加的目的将永远不能实现

问题17:用putchar()函数打印数字的正确用法及解析
1.用putchar打印: 数字0xa/b/c...+ '0'
数字0xa/b/c...- 0xa + 'a'
探究打印结果,具体实施如下:
putchar(0xa + '0'); //相当于字符‘0’加10,即变为字符':'
putchar(0xf + '0'); //相当于字符‘0’加15,即变为字符'?'
putchar(0xa + - 0xa + 'a'); //字符‘a’加0,即变为字符'a'
putchar(0xf + - 0xa + 'a'); //字符‘a’加5,即变为字符'f'
打印结果:
:?af
解析如上。
ASCII码(从左到右由小到大依次加1):0 1 ... 9 : ; < = > ? @ A B ... Y Z [ \ ] ^ _ ' a b c ...  y z { | } ~ DEL
2.用printHex()函数打印十六进制格式数字
/*十六进制的格式整型数字的输出
 *所有输入的数字都以十六进制输出
 */
void printHex(unsigned int val)
{
	int i;
	unsigned char a[8];
	//先把整形术就val装到数组a内
	for(i = 0; i < 8; i++)
	{
		a[i] = val & 0xf;
		val >>= 4;
	}
	puts("0x");
	for(i = 7; i >= 0; i--)
	{
		if(a[i] >= 0 && a[i] <= 9)	//warning: comparison is always true due to limited range of data type原因:a[i]>=0总满足;
			putchar(a[i] + '0');
		if(a[i] >= 0xa && a[i] <= 0xf)
			putchar(a[i] - 0xa + 'a');
	}
}
探究打印结果,具体实施如下:
printHex(g_i);
printHex(g_j);
printHex(g_k);
printHex(g_l);
打印结果如下:
0x8b9bf99f
0xabcdef12
0x00000012
0x0000000c

问题18:start.S中,以4字节对齐读写操作cpy/clean,会不会造成data/bss段甚至别的段开头或者结尾误操作?
答:会的,所以,data/bss段开头,甚至每个段的开头都自动设置一次4字节对齐


  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值