发表时间: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
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14