知识屋:更实用的电脑技术知识网站
所在位置:首页 > 科技

keil实验c语言汇编实验一实验报告,嵌入式系统实验

发表时间:2022-03-26来源:网络

实验四:C 语言开发/模数转换(ADC)实验

一、实验目的

1、掌握

C 语言嵌入式开发的基本步骤和原理。

2、学习使用

C 语言操作外设的方法。

3、掌握模数转换器外设的操作原理和编程。

二、实验设备

1、硬件:PC

机一台 、Mini2440 ARM

实验板一套 J-link

仿真器一套

2、软件:WindowsXP 系统,Keil uVision 4.0

集成开发环境

三、实验内容

(1)建立汇编-C

程序框架。

(2)使用

ADC 读取 Mini2440

实验板上的滑动变阻器产生的电压值。

四、实验预习要求

(1)学习

ARM 子程序调用相关的指令;

(2)查阅

S3C2440 芯片手册,了解模数转换器系统的结构和原理。

五、实验步骤

(一)C 语言编程实验:

(1)实验程序分为汇编语言和

C 语言两部分。汇编语言程序是处理器上电复位后执行的第一段程序,负责最基础的硬件初始化,在本实验中主要是初始化栈指针(SP)寄存器,为调用

C 语言函数做准备,然后跳转到 C 语言 main 函数执行;从 main

函数开始,程序的编写结构和执行原理基本上与 PC

机上典型的 C语言相同。

(2)启动

Keil uVision,新建一个工程

ex04-1。不需要系统提供的

Startup文件。建立汇编源文件

ex04-1.s,编写实验程序,然后添加到工程中。设置工程选项,存储器映射。设置工程调试选项。建立仿真初始化文件

RAM.ini。

具体步骤参考实验二。

(3)

建立 C 语言源文件 main.c,编写实验程序,然后添加到工程中。

(4)编译程序,

使用仿真器在目标板上调试运行程序, 使用单步、 设置断点,观察程序执行的流程,观察函数调用过程中栈指针寄存器(R13,SP)的值的变化。

(5)编程并观察

C 语言语句编译后对应的 ARM

汇编程序。

(二)模数转换器(ADC)实验:

(1)

本实验使用 S3C2440

片内模数转换器。从结构图和芯片手册可以知道, 该 ADC 模块总共有 8 个通道可以进行模拟信号的输入, 分别是 AIN0、AIN1、AIN2、AIN3、YM、YP、

XM、XP。模拟信号从任一通道输入,然后设定寄存器中预分频器的值来确定

AD 转换器频率,最后 ADC

将模拟信号转换为数字信号保存到 ADC

数据寄存器 0 中(ADCDAT0),然后

ADCDAT0 中的数据可以通过中断或查询的方式来访问。对于 ADC 的各寄存器的操作和注意事项请参阅数据芯片手册。本实验使用通道 0 采集实验板上的滑动变阻器的分压电压值.

(2)启动

Keil uVision,新建一个工程

ex04-2。不需要系统提供的

Startup文件。建立汇编源文件

ex04-2.s,编写实验程序,然后添加到工程中。设置工程选项,存储器映射。设置工程调试选项。建立仿真初始化文件

RAM.ini。

(3)实验程序实现了一个用于控制读取

ADC 指定通道的值的函数: int ReadAdc(int

ch);

在对 preScaler 变量初始化之后,即可通过调用该函数来多次采集连接在ADC 上的模拟信号值。

(5)

编译链接工程。连接实验板电源、J-link

仿真器,进行仿真调试。单步运行程序, 并改变板上滑动变阻器 W1 的阻值, 观察采集到的电压数值的变化。

六、实验程序

C

语言实验程序见程序清单 4.1、4.2。模数转换器实验程序见程序清单

4.3。

程序清单 4.1 C

语言实验程序(汇编部分)

Stack_Top

EQU

0x1000 - 4

;end of ram

AREA RESET, CODE, READONLY

;声明代码段RESET

ENTRY

;表示程序入口

CODE32

;声明32位ARM指令

b Reset

Undef b Undef

;handler for Undefined mode

SWI

b SWI

;handler for SWI interrupt

Pabort b Pabort

;handler for PAbort

Dabort b Dabort

;handler for DAbort

b .

;reserved

IRQ

b IRQ

;handler for IR

FIQ

b FIQ

;handler for FI

Reset

;initialize stack

pointer

LDR

R0, =Stack_Top

MOV

SP, R0

IMPORT

main

LDR

R0, =main

BX

R0

b .

;should never get here

END

程序清单 4.2 C

语言实验参考程序(C 语言部分)

int compare( int a, int b)

{

int tmp, ret;

tmp =

a - b;

if( tmp >

0)

ret = 1;

else if( tmp ==

0)

ret = 0;

else

ret = -1;

return

ret;

}

int max( int a, int b)

{

int tmp1;

if( compare( a, b) >=

0)

tmp1 = a;

else

tmp1 = b;

return tmp1;

}

main()

{

int v = 0;

v = max( v, 4);

while(1)

{

}

}

程序清单 4.3

模数转换器实验参考程序(C 语言部分)

#define rADCCON

(*(volatile unsigned *)0x58000000) //ADC

control

#define rADCTSC

(*(volatile unsigned *)0x58000004) //ADC touch screen

control

#define rADCDLY

(*(volatile unsigned *)0x58000008) //ADC start or Interval

Delay

#define rADCDAT0

(*(volatile unsigned *)0x5800000c) //ADC conversion data

0

#define rADCDAT1

(*(volatile unsigned *)0x58000010) //ADC conversion data

1

#define rADCUPDN

(*(volatile unsigned *)0x58000014) //Stylus

Up/Down interrupt status

#define LOOP 10000

#define ADC_FRE

volatile unsigned

preScaler;

int ReadAdc(int ch)

{

int

i;

static int

prevCh=-1;

rADCCON =

(1

int v = 0;

preScaler = 12000000/ADC_FREQ - 1;

//PCLK:12MHz

while(1)

{

v = ReadAdc(0);

//对应开发板上W1可调电阻;

}

}

七、实验现象

1.

C语言指令被编译成一系列的汇编指令,在运行子函数和声明变量的时候,SP会发生变化。

2.

全速运行,转动可调电阻器,暂停程序,观察局部变量,v的值发生变化,变化范围0-0x3ff。

八、思考题

(1)实验程序中

main()函数名是否可以改为其它名字?

答:实验中main()函数名可以改为其他名字。

相比于C语言程序,main()函数作为整个程序的入口,提供了很重要的信息:系统应该从main()函数处执行。所以把C语言中的main()改为其他名字后,可以通过编译,但是链接时就会出错,应为链接器不知道整个程序的入口在哪。

而实验程序中,实验的开发环境虽然用C的编译器,但却不用链接目标文件,因为最终生成的目标文件是通过汇编语言导入的:

IMPORT

main

LDR

R0, =main

BX

R0

实际导入的不是函数的.c文件,而是目标文件,所以只要目标文件的名字与上述指令中被导入的目标文件相同即可,也就没有了C语言中必须有main()的限制。例如将main()函数改为example(),则上述三条指令变为:

IMPORT

example

LDR

R0, =example

BX

R0

用汇编语言写的启动代码执行时,会将上述目标文件导入,然后同过BX指令跳转到函数头部开始执行,这一分支指令相当于链接程序时生成的程序入口信息。

(2)如何校准模数转换器测量电压的精度?

答:假设模数转换的精度为n位(mini2440最大为10),在测量精度准确的情况下,输入的电压U和转换后的数值VALUE应该满足如下关系:

如果测量出现了误差,比如电阻阻值发生变化而导致滑动变阻器调到最小值时输出的VALUE不是0,跳到最大值时输出的VALUE不是1111111111,此时可以通过如下办法校准:

首先把滑动变阻器阻值调到最大,A/D转换器的输出VALUE为MAX;然后把滑动变阻器阻值调到最小,A/D转换器的输出VALUE为MIN。

然后对A/D转换器输入电压U,A/D转换器输出结果为n位的VALUE,对应于电压U的真值为TRUEVALUE,则应有如下等式:

化简则有:

即真正的转换值应该是:

当校正以后,只需记住MAX和MIN的值,然后在程序中实现上述等式即可。

九、选做题

(2) 用板上滑动变阻器控制指示灯的闪烁速度。

滑动变阻器控制的模拟信号经A/D转换器转换为制定精度的数字信号,实验中用C语言完成A/D转换器的相关控制部分,C子函数中用return()语句返回A/D转换器转换后的信号,根据C语言汇编后的特点,一个函数返回值会用寄存器R0保存返回值。因此A/D转换器的转换结果在寄存器R1中。

要用实验开发板上的滑动变阻器控制指示灯的闪烁速度,即用模拟信号控制指示灯的闪烁速度。灯需要加入额外的代码实现GPIO功能,灯的闪烁可以用循环方式,每隔一段时间改变一次灯的状态。而控制其闪烁时间可以再嵌套循环,在内层循环中以A/D转换器的输出结果R0作为延时变量。

这样就实现了用滑动变阻器控制指示灯的闪烁速度,完整的实验程序如下:

汇编部分

Stack_Top EQU 0x1000 - 4

;end of ram

AREA RESET, CODE, READONLY

;声明代码段RESET

ENTRY

;表示程序入口

CODE32

;声明32位ARM指令

b

Reset

Undef b

Undef

;handler for Undefined mode

SWI b

SWI

;handler for SWI interrupt

Pabort

b Pabort

;handler for PAbort

Dabort

b Dabort

;handler for DAbort

b .

;reserved

IRQ b

IRQ

;handler for IR

FIQ b

FIQ

;handler for FI

Reset

LDR R0,

=Stack_Top

;initialize stack pointer

MOV SP,

R0

IMPORT

main

LDR R0,

=main

BX

R0

b .

;should never get here

END

C语言部分

#define

rGPBCON (*(volatile unsigned *)0x56000010

//Port B control register

#define

rGPBDAT (*(volatile unsigned *)0x56000014

//Port B data register

#define

rADCCON (*(volatile unsigned *)0x58000000) //ADC

control

#define

rADCTSC (*(volatile unsigned *)0x58000004)

//ADC touch screen control

#define

rADCDLY (*(volatile unsigned *)0x58000008)

//ADC start or Interval Delay

#define

rADCDAT0 (*(volatile unsigned *)0x5800000c) //ADC conversion data

0

#define

rADCDAT1 (*(volatile unsigned *)0x58000010) //ADC conversion data

1

#define

rADCUPDN (*(volatile unsigned *)0x58000014) //Stylus Up/Down

interrupt status

#define

LOOP 10000

#define

ADC_FRE

volatile unsigned preScaler;

int

ReadAdc(int ch)

{

int

i;

static

int prevCh=-1;

rADCCON

=

(1

int v =

0;

int i=0;

//灯的延时变量

int flag=0;

//控制灯的掩码

preScaler = 12000000/ADC_FREQ - 1;

//PCLK:12MHz

rGPBCON=0x15400;

//set GPIO portB(5,6,7,8) as output

while(1)

{

v = ReadAdc(0);

//对应开发板上W1

可调电阻;

if(flag==1)

flag=0;

else

flag=1;

rGPBDAT=flag&0x1e0;

//写入灯的数据为0或0x1e0,即灯的亮灭

for(i=0;i

//灯的状态延时

{

while(i

收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜