zoukankan      html  css  js  c++  java
  • Mini2440裸机开发之模数转换开发

    一、硬件介绍

    1.1 S3C2440 ADC概述

    模数转换器即A/D转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。S3C2440集成了8通道10位CMOS A/D转换器,最大转换率为2.5MHz A/D转换器时钟下的500KSPS。A/D转换器支持片上采样-保持功能和掉电模式的操作。

    对于数字信号我们需要得到它的2个属性:

    1) 转换精度:
    用多少位来存储这个数据。

    2) 采样/转换速度。

    S3C2440 ADC具有以下特性:

    • 分辨率:10 位
    • 差分线性误差:± 1.0 LSB
    • 积分线性误差:± 2.0 LSB
    • 最大转换率:500 KSPS
    • 功耗低
    • 供电电压: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 时钟下,因此转换率可以达到500 KSPS。

    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,
    则ADC 频率<2MHz)

    0xFF
    SEL_MUX [5:3]

    模拟输入通道选择

    000 = AIN0  001 = AIN1   010 = AIN2   011 = AIN3
    100 = YM    101 = YP       110 = XM     111 = XP

    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 方向模式、自动方向模式
    →ADC 转换启动延时值。
    注意:不要使用0 这个值(0x0000)

    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方向测量
    10 = Y 方向测量 11 = 等待中断模式

    -
    保留 [11:10]

    保留

    -

    XPDATA
    (正常ADC)

    [9:0]

    X 方向转换数值(包括正常ADC转换数值)
    数值范围:0 至3FF

    -

    等到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方向测量
    10 = Y 方向测量 11 = 等待中断模式

    -
    保留 [11:10]

    保留

    -

    XPDATA

    [9:0]

    Y 方向转换数值
    数值范围:0 至3FF

    -

    三、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/arm4.3.2/lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a这个位置的libgcc.a文件

    同时需要引入lib1funcs.S库文件。

    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

    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 = 100MHZ, 设置输入源, 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;
    }

    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 
    ", (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处运行。

    参考文章

    [1]S3C2440 ADC详解

  • 相关阅读:
    概述反射和序列化
    读书笔记6pandas简单使用
    读书笔记5基于matplotlib画图
    读书笔记4数据的读入和保存
    读书笔记3数组的一些常用函数
    introduction to python for statistics,analysis笔记3
    introduction to python for statistics,analysis笔记2
    introduction to anaconda
    图像的线性空间滤波matlab实现
    C-I/O操作函数详解
  • 原文地址:https://www.cnblogs.com/zyly/p/15417577.html
Copyright © 2011-2022 走看看