Mini2440裸机开发之模数转换开发
一、硬件介绍
1.1 S3C2440 ADC概述
模数转换器即A/D转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。S3C2440集成了8通道10位CMOS A/D转换器,最大转换率为2.5MHz A/D转换器时钟下的500KSPS。A/D转换器支持片上采样-保持功能和掉电模式的操作。
对于数字信号我们需要得到它的2个属性:
- 转换精度:用多少位来存储这个数据;
- 采样/转换速度;
S3C2440 ADC具有以下特性:
- 分辨率:10 位
- 差分线性误差:± 1.0 LSB
- 积分线性误差:± 2.0 LSB
- 最大转换率:500 KSPS,KSPS,全称kilo Samples per Second,即采样千次每秒,是转化速率的单位
- 功耗低
- 供电电压:3.3V
- 模拟输入范围:0 至3.3V
- 片上采样-保持功能
- 普通转换模式
- 分离的X/Y方向转换模式
- 自动(顺序)X/Y方向转换模式
- 等待中断触发
从上图可以看出AD converter前有一个多路选择器,用来选择模拟输入源,这里选择A[3:0]作为模拟输入源。这里的XP、XM、YP、YM引脚是适用于电阻屏的触摸屏触摸检测。
1.2 A/D转换时间
当PCLK 频率在50MHz 并且预分频器的值为49 时,共10 位的转换时间如下:
$$A/D转换器速率=\frac{50MHZ}{(49+1)}=1MHZ$$
$$转换时间=\frac{1}{1MHZ/5周期}=\frac{1}{200kHz}=5us$$
注释: 此A/D 转换器被设计为最高工作在2.5MHz 时钟下,因此转换率可以达到500KSPS。
1.3 Mini2440 ADC电路
Mini2440开发板引出4路A/D(模数转换)转换通道,它们位于板上的CON4-GPIO接口。为了方便测试, AIN0连接到了开发板上的可调电阻W1,原理图如下所示:
如图,是把可变电阻上的电压值变换的模拟信号通过ADC转换,输出数字信号。
二、寄存器
2.1 ADC控制寄存器(ADCCON)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
ADCCON | 0x5800000 | R/W | ADC 控制寄存器 | 0x3FC4 |
寄存器位信息:
ADCCON | 位 | 描述 | 初始状态 |
ECFLG | [15] |
转换结束标志位(只读) 0 = A/D 正在转换 1 = A/D 转换已结束 |
0 |
PRSCEN | [14] |
A/D 转换器预分频器使能 0 = 禁止 1 = 使能 |
0 |
PRSCVL | [13:6] |
A/D 转换器预分频值 数值范围: 0 至255 注意:ADC 频率应该设置为低于PCLK 的1/5。(例如PCLK=10MHz, |
0xFF |
SEL_MUX | [5:3] |
模拟输入通道选择 000 = AIN0 001 = AIN1 010 = AIN2 011 = AIN3 |
0 |
STDBM | [2] |
待机模式选择 0 = 正常工作模式 1 = 待机模式 |
1 |
READ_ START | [1] |
读启动A/D 转换 0 = 禁止读启动操作 1 = 使能读启动操作 |
0 |
ENABLE_START |
[0] |
使能A/D 转换启动。如果READ_START 为使能,则此值无效 0 = 无操作 1 = A/D 转换启动且此位在启动后被清零 |
0 |
注意:
- 当触摸屏引脚(YM、YP、XM 和XP)为禁止时,这些端口可以被用于ADC 的模拟输入端口(AIN4、AIN5、AIN6 和AIN7)。
- 当从待机模式中变换到正常工作模式时,ADC 的预分频器必须在最后的3 个ADC 时钟前使能。
- 注意位[1]这一位, 这说明转换完成读取的时候还可以触发转换,也就是相当于连续转换。
这里我们设置预分频器使能,PCLCK=50MHZ,设置预分频器值为49:
$$A/D转换器速率=\frac{50MHZ}{(49+1)}=1MHZ$$
模拟输入通道选择AIN0:
ADCCON = (1<<14) | (49<<6) | (0<<3); //设置输入源AIN0, ADC时钟为1Mhz
位0用来使能A/D转换功能,A/D转换成功后ECFLG会设置为1。
2.2 ADC 启动延时寄存器(ADCDLY)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
ADCDLY | 0x5800008 | R/W | ADC 启动或初始化延时寄存器 | 0x00ff |
寄存器位信息:
ADCDLY | 位 | 描述 | 初始状态 |
DELAY | [15:0] |
正常转换模式、XY 方向模式、自动方向模式 |
0x00ff |
2.3 ADC 转换数据寄存器(ADCDAT0)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
ADCDAT0 | 0x580000C | R/W | ADC 转换数据寄存器 | - |
寄存器位信息:
ADCDAT0 | 位 | 描述 | 初始状态 |
UPDOWN | [15] |
等待中断模式中笔尖的起落状态 0 = 笔尖落下态 1 = 笔尖抬起态 |
- |
AUTO_PST | [14] |
自动顺序X 方向和Y 方向转换 0 = 正常ADC转换 1 = 顺序X 方向、Y 方向测量 |
- |
XY_PST | [13:12] |
手动X 方向或Y 方向测量 00 = 无操作模式 01 = X方向测量 |
- |
保留 | [11:10] |
保留 |
- |
XPDATA |
[9:0] |
X 方向转换数值(包括正常ADC转换数值) |
- |
等到A/D转换完成,读取XPDATA[9:0]可以获取到转换的数据。
2.4 ADC 转换数据寄存器(ADCDAT1)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
ADCDAT1 | 0x5800010 | R/W | ADC 转换数据寄存器 | - |
寄存器位信息:
ADCDAT1 | 位 | 描述 | 初始状态 |
UPDOWN | [15] |
等待中断模式中笔尖的起落状态 0 = 笔尖落下态 1 = 笔尖抬起态 |
- |
AUTO_PST | [14] |
自动顺序X 方向和Y 方向转换 0 = 正常ADC 转换 1 = 顺序X 方向、Y 方向测量 |
- |
XY_PST | [13:12] |
手动X 方向或Y 方向测量 00 = 无操作模式 01 = X方向测量 |
- |
保留 | [11:10] |
保留 |
- |
XPDATA |
[9:0] |
Y 方向转换数值 |
- |
三、ADC代码
3.1 ADC初始化步骤
- GPIO设置,设置为模拟输入,由于ADC有单独的引脚,没有复用GPIO,所以这一步省略;
- 使能分频器,设置分频系数;
- 选择模拟输入通道; 使能A/D转换;
- 等待转换完成,读取数据(为了得到准确的数据,可以多次转换取平均);
3.2 代码
下载文章最后附加的代码,如果出现编译错误:
'__aeabi_fdiv`问题主要是由于硬件不支持浮点运算,所以采用软件模拟浮点运算,解决方案是引入libgcc.a等库文件。参考文章arm-linux-gcc 4.4.3版本解决`__aeabi_uidivmod'和 `__aeabi_uidiv'问题方法,不过这篇博客给的libgcc.a库文件错了,应该使用/usr/local/arm/4.3.2/lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a这个位置的libgcc.a文件。
同时需要引入lib1funcs.S库文件。
3.2.1 adc.h
/***************************************************************************************************************** * * FileName : adc.h * Function : 数模转换器设置 * Author : zy * ****************************************************************************************************************/ #ifndef __ADC_H__ #define __ADC_H__ #include "type.h" /******************************************************************************************************************/ /* ADC相关寄存器宏定义 */ #define ADCCON (*(volatile u32 *)0x58000000) /* ADC 控制寄存器 */ #define ADCTSC (*(volatile u32 *)0x58000004) /* ADC 触摸屏控制寄存器 */ #define ADCDLY (*(volatile u32 *)0x58000008) /* ADC 启动延时寄存器 */ #define ADCDAT0 (*(volatile u32 *)0x5800000C) /* ADC 转换数据寄存器 */ #define ADCDAT1 (*(volatile u32 *)0x58000010) /* ADC 转换数据寄存器 */ #define ADCUPDN (*(volatile u32 *)0x58000014) /* ADC 触摸屏起落中断检测寄存器 */ /* ADC转换通道 */ typedef enum { AIN0 = 0x00, AIN1 = 0x01, AIN2 = 0x02, AIN3 = 0x03, YM = 0x04, YP = 0x05, XM = 0x06, XP = 0x07 }ADC_CHANCAL; /* 函数声明 */ extern void adc_init(ADC_CHANCAL chancal); /* 启动ADC */ extern u16 adc_read(); /* 采用置位使能方式启动AD转换,16次采样取平均值 */ extern float adc_real_value(float max, u16 adc);; /* 计算真实值 */ #endif
3.2.2 adc.c
/************************************************************************** * * FileName : led.c * Function : LED控制 * Author : zy * *************************************************************************/ #include "adc.h" /************************************************************* * * Function : 启动ADC * Input : ch : 转换通道 * **************************************************************/ void adc_init(ADC_CHANCAL ch) { ADCCON = (1 << 14) | (49 << 6) | (ch << 3); /* PCLK = 50MHZ, 设置输入源, ADC时钟为1Mhz */ } /************************************************************* * * Function : 采用置位使能方式启动AD转换,16次采样取平均值 * Input :ch : 转换通道 * Return : 精度为10:成功返回0~1023 * **************************************************************/ u16 adc_read() { int i; int val = 0; for (i = 0; i < 16; i++) { ADCCON |= 0x1; /* 使能ADC转换 */ while (ADCCON & 0x1); /* 判断使能ADC转换后被清零 */ while (!(ADCCON & 0x8000)); /* 等待转换结束 */ val += (ADCDAT0 & 0x3ff); /* 读取ADC转换的值 */ } val = val >> 4; /* 计算ADC转换值 */ return val; } /************************************************************* * * Function : 计算真实值 最大值*(adc值)/1024 * Input : max : 真实最大值 * adc : 采样得到的值 * S3C2440硬件不支持浮点运算 所以这里调试时会报错 * **************************************************************/ float adc_real_value(float max, u16 adc) { float tmp = (max * (float)adc) / 1024.0; return tmp; }
3.2.3 main.c
#include "led.h" #include "lcd.h" #include "adc.h" #include "common.h" #include "uart.h" int main() { float voltage; int value; int tmp; u8 real_value[16] = { 0 }; led_init(); // 初始化lcd lcd_init(); uart_init(); // 初始化adc adc_init(AIN0); while(1) { led_turn(LED1); /* adc读取电阻两端电压 */ value = adc_read(); voltage = adc_real_value(3.3, value); tmp = (voltage - (int)voltage) * 1000; /* 计算小数部分, 本代码中的printf无法打印浮点数 */ printf("AIN0 = %d.%-3dV \r\n", (int)voltage, tmp); /* 浮点数转为字符串 */ float_to_str(voltage, real_value); strcat(real_value, "V"); lcd_clear_srceen(0x00); lcd_draw_char_lib(100, 100, 0xFF0000, real_value,strlen(real_value)); delay_ms(4000); } return 0; }
所以,调试代码,通过串口输出:
同时在LCD显示屏也会输出电压值。
四、源码下载
Young / s3c2440_project【8.adc】
这个代码已经远超过4kb,并且没有将代码从NAND拷贝到SDRAM运行,只可以下载到SDRAM 0x30000000处运行。
参考文章