目录:
问题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启动,视频例程:成立,系统是否会判断为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启动就废了
从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)问题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:操作寄存器的规范
改变寄存器的话。读 改 写 三步。别影响其他位。
对某个寄存器进行操作时,先对该寄存器清零,再赋值。
问题8:ARM架构芯片(如ARM9-s3c2440a),寄存器都是32位,为什么?
答:寄存器的本质是内存,且ARM架构下固定的32 bits 操作码(opcode)长度,降低编码数量所产生的耗费,减轻解码和流水线化的负担。大多均为一个CPU周期执行。组成操作码字段的位数一般取决于计算机指令系统的规模。
_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
需要注意的是,方法2有时候对不再同一个目录的头文件包含时会出错,原因未知
例如:下面main()中使用头文件:
1.#include
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 [email protected]
%.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.htmlhttp://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 波特率分频寄存器
波特率发生
每个UART 的波特率发生器为发送器和接受器提供串行时钟。波特率发生器的源时钟可以选择 S3C2440A 的内部系统时钟或 UEXTCLK。换句话说,分频由设置 UCONn 的时钟选项选择。波特率时钟是通过 16 和由 UART波特率分频寄存器(UBRDIVn)指定的 16 位分频系数来分频源时钟(PCLK,FCLK/n 或 UEXTCLK)产生的。= 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个字节的内存被该函数赋值重新装数;
当只有一个字符自加结果打印时,只会输出被修改后的数,而主程序要实现字符自加的目的将永远不能实现
当有多个字符自加结果打印时,每次打印两个被修改的数,而主程序要实现字符自加的目的将永远不能实现
/*十六进制的格式整型数字的输出
*所有输入的数字都以十六进制输出
*/
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');
}
}
探究打印结果,具体实施如下: