2015-12-15 09:55:21 u013151320 阅读数 8073
  • AD和DA转换-第1季第16部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第16个课程,主要讲解AD转换和DA转换。目标是理解模拟量和数字量的概念,并且学会使用AD转换来采集现实世界的模拟量。

    2573 人正在学习 去看看 朱有鹏

PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。

AD转换即(模拟量)Analog 转换成 (数字量)Digital

1.引脚分析

这里写图片描述

PCF8591具有4个模拟输入(AIN0~AIN3)、1个模拟输出(AOUT)和1个串行I²C总线接口(SDA、SCL)。
PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。
OSC:外部时钟输入端,内部时钟
EXT:内部、外部时钟选择线,使用内部时钟时 EXT 接地。
VDD、VSS:电源端。
AGND:模拟信号地。
VREF:基准电源端。

2.应用分析

AD转换部分电路图:

这里写图片描述

要求:使用LCD1602将电压值显示出来,调节变阻器改变电压值

说明:以下代码中用到了LCD1602驱动程序和I2C总线驱动程序,请参考博主51单片机栏目下的博客。

#define uint unsigned int
#define uchar unsigned char
/*查阅pcf8591的资料可知它的都写地址如下*/
#define  WRITEADDR 0x90    //写地址
#define  READADDR  0x91    //读地址
uchar dat[6];

void Pcf8591SendByte(uchar channel)
{   
    I2C_Start();    //关于I2C总线,请参照博主博文:51单片机I2C总线驱动程序
    I2C_SendByte(WRITEADDR);        //发送写器件地址
    I2C_SendByte(0x40|channel);     //发送控制寄存器
    I2C_Stop();
}

uchar Pcf8591ReadByte()
{
    uchar num;
    I2C_Start();
    I2C_SendByte(READADDR);      //发送读器件地址
    dat=I2C_ReadByte();          //读取数据
    I2C_Stop();                  //结束总线
    return num;
}

void main()
{
    uint adNum,i;
    float value;
    Lcd1602_init();   //关于lcd1602,请参照博主博文:51单片机LCD1602程序详解
    while(1)
    {
            Pcf8591SendByte(0);      //使用通道0
            /*adNum一定是0到255之间的一个数,因为pcf8591是8位的AD/DA芯片,所以输出的范围为00000000到11111111,即0到255*/
            adNum=Pcf8591ReadByte();   //读出数值
            /*将adNum转换成电压值,单片机的电压为5V,则电位器的电压为0到5V,用0到255表示0到5V,则每一个1代表5/255V,即0.01953V*/
            value=adNum*0.01953;  //转为电压值
            adNum=value*100;        //保留两位小数,以便显示出来

            dat[0]=adNum/1000+'0';      //加上'0'是表示数字转换成字符,1602只能显示字符
            dat[1]=adNum%1000/100+'0';
            dat[2]='.';
            dat[3]=adNum%100/10+'0';
            dat[4]=adNum%10+'0';
            dat[5]='V';
            Lcd1602_WriteCom(0x80);    
            for(i=0;i<6;i++)
            {
               Lcd1602_WriteData(dat[i]);
            }
    }
}
2014-08-22 14:03:38 bluewhaletech 阅读数 2285
  • AD和DA转换-第1季第16部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第16个课程,主要讲解AD转换和DA转换。目标是理解模拟量和数字量的概念,并且学会使用AD转换来采集现实世界的模拟量。

    2573 人正在学习 去看看 朱有鹏

S12系列单片机的AD转换特性

AD转换就是模数转换,就是把模拟信号转换成数字信号。Freescale的S12系列单片机的AD转换模块有以下特点:

1) 转换精度可配置:8位/10位/12位

2) 在STOP模式下,使用内部时钟可以进行AD转换

3) 完成AD转换后自动进入低功耗模式

4) AD采样时间可配置

5) 转换结果数据可配置左对齐或右对齐

6) 可配置转换完成产生中断

7) 模拟输入的8通道可复用

8) 转换序列长度可选1-8

9) 连续转换模式

10)多通道扫描

 

S12系列单片机寄存器介绍

下面以MC9S12HY64芯片为例介绍AD转换相关的常用寄存器,Freescale其他8位/16位芯片的AD转换寄存器也类似

1)控制寄存器ATDCTL0

WRAP:设置多通道转换模式下的环绕通道,在多通道(MULT = 1)模式下,当设置的环绕通道AD转换完成后通道会转到AN0,例如WRAP设置为AN5,那么AN5通道AD转换完成后,AD转换通道就转到AN0,AN6和AN7就不会进行AD转换。

2)控制寄存器ATDCTL1

ETRIGSEL,ETRIGCH:设置外部触发源

SRES:转换精度,可配置为8位/10位/12位

3)控制寄存器ATDCTL2

AFFC:AD转换完成快速标志位清零,AFFC=0状态标志位需手动清零,AFFC=1每次读取结果寄存器标志位自动清理

ICLKSTP:STOP模式下AD转换采用内部时钟工作使能位

ETRIGLE,ETRIGP:外部触发类型设置

ETRIGE:外部触发模式设置

ASCIE:AD转换序列完成中断使能位

ACMPIE:AD比较中断使能位

4)控制寄存器ATDCTL3

DJM:转换结果对齐方式,DJM=0转换结果左对齐,DJM=1转换结果右对齐

S8C,S4C,S2C,S1C:转换序列的长度,如果多通道一次只转换AN4-AN7,那长度就配置成4,如果是单通道,配置成4,那就是对转换的通道连续转换4次

FIFO:AD转换结果的存储模式,AD转换的结果按顺序存在结果寄存器中,当FIFO=0时,每次AD转换序列开始都是从ATDDR0开始存储转换结果,当FIFO=1时,每次AD转换序列开始都是从上一个序列结束时的存储结果的寄存器的后一个开始,所有转换结果都是循环存储(即存储到ATDDR9后,返回ATDDR0,再从ATDDR0-ATDDR9存储),例如要转换的为通道5、6、7,转换模式为连续转换(SCAN=1),当FIFO=0时,每次转换序列的转换结果都是存储在ATDDR0-ATDDR2中;当FIFO=1时,第一次转换序列的结果存储在ATDDR0-ATDDR2中,第二次转换序列的结果存储在ATDDR3-ATDDR5中,以此类推

ETRIGE:外部触发模式设置

ASCIE:AD转换序列完成中断使能位

ACMPIE:AD比较中断使能位

5)控制寄存器ATDCTL4

SMP:采样时间

PRS:AD转换器时钟预分频,fATDCLK=fBUS/(2*(PRS+1))

6)控制寄存器ATDCTL5

ATDCTL5寄存器写入值后会停止当前的AD转换并开始新的AD转换

SC:特殊通道转换位

SCAN:连续转换模式,SCAN=0单次转换,SCAN=1连续转换

MULT:多通道模式,MULT=0单通道转换,MULT=1多通道转换

CD,CC,CB,CA:转换通道选择,对于单通道模式,确定AD转换的通道;对于多通道模式,确定AD转换的首通道

7)状态寄存器ATDSTAT0

SCF:转换序列完成标志位,SCF为0表示当前转换序列未完成,为1表示当前转换序列完成。当出现以下情况时,SCF 标志位清零a.寄存器写入1;b. ATDCTL5写入值;c. AFFC=1并读取结果寄存器。

ETORF:外部触发覆盖标志位

FIFOR:结果寄存器覆盖写入标志位

CC:转换计数器,只读,代表存放当前AD转换结果的结果寄存器

8)状态寄存器ATDSTAT2

CCF:独立通道转换完成标志位

9)AD数字输入信号使能寄存器ATDDIEN

IEN:输入信号输入使能位

10)结果寄存器ATDDR0- ATDDR9

AD转换结果寄存器,左对齐结果数据存储格式如图1所示:


图1

右对齐结果数据存储格式如图2所示:


图2


S12系列单片机AD转换的应用举例

了解了AD转换模块的相关寄存器,就可以根据实际工程的需要来配置相应的寄存器,实现符合项目需求的AD转换功能呢,主要需要配置采样时间,使用的AD转换通道,转换结果的对齐方式,是否需要产生中断等

例如,如果当前项目采用了AD5,AD6,AD7三路AD转换,这三路AD转换精度为12位,不采用中断方式,那么根据这些需求,我们就可以对AD模块进行初始化及对结果进行查询读取,以下是相关代码

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose:  A/D Convertion Driver
* @Author:  Purple
* @Version:  1.0
* @Date:  Create By Purple 2014.08.20
* 
* 
* Copyright (C) BlueWhale Tech.   
* All rights reserved.  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


#ifndef A2DDRV_H
#define A2DDRV_H


/* Include Files */


/* Macros */
#define A2D_BEGIN_CHANNEL  5
#define A2D_END_CHANNEL  7
#define A2D_CHANNEL_NUMS  (A2D_END_CHANNEL-A2D_BEGIN_CHANNEL+1)

/* Variables */
extern unsigned short int A2DResults[A2D_CHANNEL_NUMS];


/* Function Prototypes */
void A2DInit(void);
void A2DConvert(void);


#endif
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose:  A/D Convertion Driver
* @Author:  Purple
* @Version:  1.0
* @Date:  Create By Purple 2014.08.20
* 
* 
* Copyright (C) BlueWhale Tech.   
* All rights reserved.  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


/* Include Files */
#include "A2DDrv.h"


/* Variables */
unsigned short int A2DResults[A2D_CHANNEL_NUMS];


/* Function Definitions */
/*
 * FunctionName: A2DInit
 * Purpose: A/D转换模块初始化
 * Parameters: 无
*/
void A2DInit(void)
{
	ATDDIEN = 0x00;    //AD通道输入关闭
	ATDCTL1 = 0x40;    //设置AD转换精度为12位
	ATDCTL2 = 0x00;
	ATDCTL3 = 0x80;    //转换结果右对齐,FIFO模式关闭
	ATDCTL3 |=((unsigned char)A2D_CHANNEL_NUMS<<3);    //设置转换序列的长度
	ATDCTL4 = 0x63;    //设置AD采样时间
	ATDCTL5 |= AD_BEGIN_CHANNEL;   //设置AD采样的首通道
	ATDCTL5_MULT=1;   //设置多通道模式,开始AD转换
}

/* Function Definitions */
/*
 * FunctionName: A2DConvert
 * Purpose: A/D转换结果查询读取
 * Parameters: 无
*/
void A2DConvert(void)
{
	unsigned char i;
	
	while(!ATDSTAT0_SCF);    //等待转换序列完成
	
	for(i=0;i

另外,使用AD转换需要注意以下几点:

1.对于AD转换的精度并不是越高越好,因为转换精度越高,转换结果波动也越大,转换精度越低,转换结果波动也越小,所以要根据项目的实际需求选择合适的转换精度。

2. AD转换使用多通道时应保持使用的通道口连续,这样会方便软件处理。



2019-12-02 20:48:41 tyfwin 阅读数 123
  • AD和DA转换-第1季第16部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第16个课程,主要讲解AD转换和DA转换。目标是理解模拟量和数字量的概念,并且学会使用AD转换来采集现实世界的模拟量。

    2573 人正在学习 去看看 朱有鹏

51单片机AD转换、串口蓝牙通讯、labview上位机显示

功能:单片机模拟数字转化,将数字信号通过蓝牙或者串口传输给电脑,电脑上labview显示出一个图表。

我这里实际使用是探测光信号强弱的:

 光信号——>光电探测器——>单片机电压转化为数字信号(AD转化)——>单片机通过串口发送出——>labview接收显示

 

单片机采用非常简单,别人常说它垃圾,可能确实有点垃圾,但是我却很喜欢的stc12c5a60s2,自带AD转化,有2个串口美滋滋,引脚图如下:

我使用P1.1口作为模拟信号输入,P1.3作为串口把数据实时传输出去。

voltagetemp_int=(int)(voltagetemp*1000);
voltagetemp_a = voltagetemp_int /100;
voltagetemp_b = voltagetemp_int %100;
UartSendChar('l');
UartSendChar('m');
UartSendChar(voltagetemp_a);
UartSendChar(voltagetemp_b);
UartSendChar('n');
UartSendChar('o');

根据上面代码,可以知道,传出去的数据,自己为了弄一个校验识别,先发送字符l和m,再发送真实数据,再发送字符n和o表示这一次数据发送结束。模拟信号电压真实值在0-5v之间,这里为了精度高一点显示,先乘以1000,再把高2位和低2位分别取出来,分别发送出去。

 

还是以前本科时候写的主要代码C语言,拿过来改改用,或许不是很规范。问题很大。懒得改了。

完事之后文件夹如下。

 

主函数代码:ceshi.c


#include <12c5a60s2.h>

#include "intrins.h"

#include <stc12delay.h>

#include <ADchange.h>

#include  <math.h>

#include  <uart.h>

int light=0;

uint scan_num=0;

float voltagetemp=0;

int  voltagetemp_int=0;

int  voltagetemp_a=0;

int  voltagetemp_b=0;

int t_int=0;

void main()

{

UartInit();

AD_init();

    while(1)

   {

scan_num++;

 if(scan_num>30)

 {

    scan_num=0;

AD_get(1);

light=initvoltage1;delay(1);

    voltagetemp=light/1024.0*5;

    voltagetemp_int=(int)(voltagetemp*1000);

    voltagetemp_a = voltagetemp_int /100;

voltagetemp_b = voltagetemp_int %100;

// UartSendChar(voltagetemp_int);

    UartSendChar('l');

UartSendChar('m');

UartSendChar(voltagetemp_a);

UartSendChar(voltagetemp_b);

UartSendChar('n');

UartSendChar('o');

 }

}

}

————

ADchange.h内容如下:

idata uint initvoltage1;

void AD_init()

{

P1ASF=0x03;  //P1  0 1 口作为模拟功能AD使用

ADC_RES=0;   //清零转换结果寄存器高8位

ADC_RESL=0; //清零转换结果寄存器低2位

ADC_CONTR=0x80;//开启AD电源  

delay(1);   //等待1ms,让AD电源稳定

}



void AD_get(char channel)  //我这里只能取0 和 1 通道

{  

ADC_CONTR=0x88|channel;    //开启AD转换1000 1000 自定通道 即POWER SPEED1 SPEED0 ADC_FLAG  ADC_START CHS2 CHS1 CHS0

_nop_(); _nop_(); _nop_(); _nop_();//要经过4个CPU时钟的延时,其值才能够保证被设置进ADC_CONTR 寄存器

while(!(ADC_CONTR&0x10));    //等待转换完成

ADC_CONTR&=0xe7;      //关闭AD转换,ADC_FLAG位由软件清0

initvoltage1=ADC_RES*4+ADC_RESL;

//voltagetemp=initvoltage1/1024.0*5;

//FtoS(voltagetemp);

//return str;   //返回AD转换完成的10位数据(16进制)

}

 

————

stc12delay.h内容如下:

 #ifndef __STC12DELAY_H__

#define __STC12DELAY_H__



#define uchar unsigned char

#define uint unsigned int





/*****相关宏定义***/

    /* exact-width signed integer types */

typedef   signed          char int8_t;

typedef   signed short     int int16_t;

typedef   signed           int int32_t;

typedef   signed       long int int64_t;



    /* exact-width unsigned integer types */

typedef unsigned          char uint8_t;

typedef unsigned short     int uint16_t;

typedef unsigned           int uint32_t;

typedef unsigned       long int uint64_t;





////11.0592    1T

void delay01ms() //@11.0592MHz

{

unsigned char i, j;



_nop_();

i = 11;

j = 190;

do

{

while (--j);

} while (--i);

}



 //延时n ms

void delaynms(unsigned int n)

{   unsigned int i;

for(i=0;i<n;i++)

delay01ms();

}

//也是延时 n  ms

void delay(unsigned int n)

{   unsigned int i;

for(i=0;i<n;i++)

delay01ms();

}



////11.0592    1T

void delay1ms12(void)   //误差 -0.018084490741us

{

    unsigned char a,b,c;

    for(c=8;c>0;c--)

        for(b=197;b>0;b--)

            for(a=2;a>0;a--);

}



void delaynms12 (unsigned int n)

{   unsigned int i;

for(i=0;i<n;i++)

delay1ms12();

}



#endif


 

 

————

uart.h内容如下:

  


void UartInit();

void UartSendChar(unsigned char ch); //向外发送字节

void UartSendStr(unsigned char *pStr); //向外发送字符串



void UartInit()

{

//串口2,用于舵机控制器

//串口1,用于接收上位机发出指令

     S2CON = 0x50; // SCON: serail mode 1, 8-bit UART  定时器1作为波特率的发生器

 SCON = 0x50;

 AUXR=0X15;

 BRT=(int)(256-((11059200/9600)/32));

 EA = 1;

 ES = 1;

}



//串口2有关

void UartSendChar(unsigned char ch)   //向外发送字节

{

    S2BUF = ch;

    while(!(S2CON&S2TI));

    S2CON&=~S2TI;

}

void UartSendStr(uchar *pStr)     //向外发送字符串

{

    while(*pStr != 0)

     {

        S2BUF = *pStr++;

        while(!(S2CON&S2TI));

     S2CON&=~S2TI;

     }

}  

把生成的hex文件下载到单片机之后,单片机就不停的向串口发送:l,m,数据1,数据2,n,o,l,m,数据1,数据2,n,o, l,m,数据1,数据2,n,o, ……

 

好,然后是labview做的上位机界面的接收,显示。

 

先看界面情况:

 

然后看前面板程序:

 

先配置串口(和单片机内配置保持一致就行了),电脑上就可以选择串口号,包括蓝牙也可以直接选择电脑蓝牙对应的串口号:

 

再经过一个while循环,循环内进行主要数据的接受和处理。先visa读取数据,这里每次读取10个数据,一边直接显示在缓存区,同时下面接一根线将其转化为图标。

 

 

下面将visa读取的数据实际上是一个字符类型的,转化为字节数组。

下面接for循环,来解析分别把这个10个元素的一维数组内的数据索引出来。

 

 

索引数组就是脚本语言中的a[5],把a数组中第6个元素取出来。

下面就分别把这个数组中,a[i],a[i+1],a[i+2],a[i+3]这4个元素分别拿出来。i是遍历的从0-9。

然后判断,如果满足a[i] =109,a[i+3]=110,(109和110分别是asc的字符mn) 那么 把a[i+1]和a[i+2]这2真实数据拿出来进行一个类型转换,转化为真实的电压数据显示出来即可。

 

完整的labview代码如下:

 

Over~

 

 

2016-08-06 17:15:40 L20902 阅读数 8339
  • AD和DA转换-第1季第16部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第16个课程,主要讲解AD转换和DA转换。目标是理解模拟量和数字量的概念,并且学会使用AD转换来采集现实世界的模拟量。

    2573 人正在学习 去看看 朱有鹏

在数逻的课程中,已经学习过AD转换的概念:将模拟信号采样、量化、编码后转换为数字信号。但是未学习过通过单片机编程,显示结果。

编码分有舍有入、只舍不入两种,量化误差前者更小。=2Vm/(2^n+1  - 1 )

注意,为了达到精确度高、稳定性好的目的,最好将所有器件的模拟地和数字分别连接,最后将模拟地和数字地仅在一点相连。

 

此处,使用的是STC12C5A60S2内部的AD转换。


/* 功能:使用12C5A60S2内部AD读取外部电压,显示在1602上       */

#include "STC12C5A60S2.H"
#include <intrins.h>
sbit RS = P2^6;   //1602定义口  //HZ:EN=P2.2  RS=P2.0  RW=P2.1
sbit RW = P2^5;
sbit EN = P2^7;
#define uchar unsigned char;
#define uint unsigned int;

#define RS_CLR RS=0 
#define RS_SET RS=1
#define RW_CLR RW=0 
#define RW_SET RW=1 
#define EN_CLR EN=0
#define EN_SET EN=1

#define DataPort P0   //连接1602数据口 P0             

uchar da1=0,da2=0,da3=0;
double Data,c;
char a[5]="";         
uchar ADC_Chanul_Turn=0;


void DelayUs2x(unsigned char t)
{   
 while(--t);
}


void DelayMs(unsigned char t)
{
     
 while(t--)
 {
     //大致延时1mS
     DelayUs2x(245);
     DelayUs2x(245);
 }
}
/*------------------------------------------------
              判忙函数
------------------------------------------------*/
 bit LCD_Check_Busy(void) 
 { 
 DataPort= 0xFF; 
 RS_CLR; 
 RW_SET; 
 EN_CLR; 
 _nop_(); 
 EN_SET;
 return (bit)(DataPort & 0x80);   
 }
/*---------
------------------------------------------------*/
 void LCD_Write_Com(unsigned char com) 
 {  
 while(LCD_Check_Busy()); //忙则等待
 RS_CLR; 
 RW_CLR; 
 EN_SET; 
 DataPort= com;          //
 _nop_(); 
 EN_CLR;
 }
/*------------------------------------------------
              写入数据函数
------------------------------------------------*/
 void LCD_Write_Data(unsigned char Data) 
 { 
 while(LCD_Check_Busy()); //忙则等待
 RS_SET; 
 RW_CLR; 
 EN_SET; 
 DataPort= Data; 
 _nop_();
 EN_CLR;
 }

/*------------------------------------------------
                清屏函数
------------------------------------------------*/
 void LCD_Clear(void) 
 { 
 LCD_Write_Com(0x01); 
 DelayMs(5);
 }
/*------------------------------------------------
              写入字符串函数
------------------------------------------------*/
 void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)//y为行数,x为横坐标,最后一个是字符 
 {     
 if (y == 0) 
     {     
     LCD_Write_Com(0x80 + x);     //表示第一行
     }
 else 
     {      
     LCD_Write_Com(0xC0 + x);      //表示第二行
     }        
 while (*s) 
     {     
 LCD_Write_Data( *s);     
 s ++;     
     }
 }
/*------------------------------------------------
              写入字符函数
------------------------------------------------*/
 void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) 
 {     
 if (y == 0) 
     {     
     LCD_Write_Com(0x80 + x);     
     }    
 else 
     {     
     LCD_Write_Com(0xC0 + x);     
     }        
 LCD_Write_Data( Data);  
 }
/*------------------------------------------------
              初始化函数
------------------------------------------------*/
 void LCD_Init(void) 
 {
   LCD_Write_Com(0x38);    /*显示模式设置*/ 
   DelayMs(5); 
   LCD_Write_Com(0x38); 
   DelayMs(5); 
   LCD_Write_Com(0x38); 
   DelayMs(5); 
   LCD_Write_Com(0x38);  
   LCD_Write_Com(0x08);    /*显示关闭*/ 
   LCD_Write_Com(0x01);    /*显示清屏*/ 
   LCD_Write_Com(0x06);    /*显示光标移动设置*/ 
   DelayMs(5); 
   LCD_Write_Com(0x0C);    /*显示开及光标设置*/
   }
   
/*------------------------------------------------
                  ADC函数
------------------------------------------------*/ 
void InitADC()//初始AD寄存器
{
         P1ASF=0x03;     //0xff设置P1口全部为ADC通道,P10 P11为输出口
        ADC_RES=0x00;    //清除高8位缓冲数据
        if(ADC_Chanul_Turn%2==0)
        {
            ADC_CONTR=0xF0;    //P10口
            _nop_();
            _nop_();
            _nop_();
            _nop_();
            ADC_CONTR=0xE8;
                
         }
        if(ADC_Chanul_Turn%2==1)   //P11口
        {
            ADC_CONTR=0xF1; 
            _nop_();
            _nop_();
            _nop_();
            _nop_();
            ADC_CONTR=0xE9;    
         }                              
}     

void timer0() interrupt 1      //interrupt 1: 定时器0,interrupt3:定时器3
{
    TH0=(65536-20000)/256;       //高八位,(需要表示Xms的定时,计数器由65536-X数到65536,由于16位,只能分高低位)
    TL0=(65536-20000)%256;     //低八位
    InitADC();
    
}

void adc_isr() interrupt 5      //FLAG标志位置位触发中断,没有设优先级,但是同优先级下定时器0更高
{  
   
   //V_5REF=V_1REF*256/da_ref;
   if(ADC_Chanul_Turn%3==0)                     //外部基准电压
   {    
       da1=ADC_RES;                                   //获取转换结果
       Data=((double)da1/256)*5;     //取八位计算基准电压Data,
       c =Data;
       } 
   if(ADC_Chanul_Turn%3==1)
   {
       da2=ADC_RES;                                  //获取转换结果
       Data=((double)da2/256)*5;     //取八位计算实际值Data,
       c =Data;
       }
   if(ADC_Chanul_Turn%3==2)
   {
       da3=ADC_RES;                                  //获取转换结果
       Data=((double)da3/256)*5;     //取八位计算实际值Data,
       c =Data;
       }
      
   a[0]=((int)c%10+0x30);//个位(电压<5,仅有个)     //0x30: ASCAI码里代表“0”,必须转换成字符存在字符型数组里才可以在1602液晶屏上显示
   a[1]=0x2e;                    //小数点
   a[2]=((int)(c*10)%10+0x30); // 十分位
   a[3]=((int)(c*100)%10+0x30);// 百分位
   a[4]='\0';                  //  加了串尾符才成了字符串哦

   if(ADC_Chanul_Turn%3==0)  LCD_Write_String(0,0,a);
   if(ADC_Chanul_Turn%3==1)  LCD_Write_String(5,0,a);
   if(ADC_Chanul_Turn%3==2) LCD_Write_String(0,1,a); 
   
   ADC_CONTR&=0xEF;               //标志位清零
   
   ADC_Chanul_Turn++;
   if(ADC_Chanul_Turn==252) 
      ADC_Chanul_Turn=0;    
}
void main()
{
    LCD_Init(); 
    LCD_Clear();                     //清屏
    DelayMs(255);
    TH0=(65536-20000)/256;           //开定时器0
    TL0=(65536-20000)%256;
    EA=1;                             //开全局中断
    ET0=1;                             //允许定时器零中断
    EADC=1;                          //允许ADC中断 
    TR0=1;       
    while(1);
}


2013-04-18 10:22:30 superanters 阅读数 17952
  • AD和DA转换-第1季第16部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第16个课程,主要讲解AD转换和DA转换。目标是理解模拟量和数字量的概念,并且学会使用AD转换来采集现实世界的模拟量。

    2573 人正在学习 去看看 朱有鹏

AD转换

    我们先看看R1和R2,R2是个可调电阻 如果我们将R2变大 RA0这个管脚上的电压就越大。R2变小 RA0这个管脚上的电压就越小。那单片机是怎么知道电压变化的。这就需要AD转换。就是将模拟量转换成数字量。

  

   PIC单片机如何表示电压

     PIC用十位二进制位的数来表示电压,也就是数值0~1023来表示电压。那比如现在这个数值是400那这代表多少的电压?这就要根据参考电压来确定了。

   比如我们设置正参考电压为3.3V ,当输入的电压为0时,数值就为0。当输入的电压为3.3V时,数值就是1023. 那如果输入的电压是1.2V代表多少电压。

  首先,先算出一个数值代表多少的电压 3.3V除以1023 约等于 0.003V .

  然后,1.2V除以0.003V 等于400. 这就得出了400代表的是1.2V。

       见下图我们可以看AN0~AN7.这些都是可以配置成模拟输入的端口。只有这些引脚才能做为AD转换的端口。


实例讲解:

 例如: 我们看第一张的原理图,从RA0/AN0脚输入个模拟量如果电压大于1.2v则LED亮否则LED灭。

AD的设置步骤:

   1,设置端口

          将RA0口设置为输入 TRISA =  0x01;

          将RA0口设置为模拟  ANSELA = 0x01;

   2, 配置ADC模块

           选择ADC的转换时钟

           如何选择转换时钟呢 要根据现在的时钟频率进行选择。可以根据数据手册中的表格进行选择 。

          我们设置单片机的时钟频率为32MHZ ,选择ADC周期关键不要选择阴影部分,在32MHz 这一列 我们随意选择了ADC时钟周期1us,对应的时钟源为Fosc/32.,AD控制寄存器1 ADCON1的ADCS<2:0>=010注:ADCS<2:0>代表的意思就是 ADCS的0到2位


             配置参考电压

            我们这里把正参考电压配置为电源压。AD控制寄存器1 ADCON1的ADPREF<1:0>=00;

            配置左/右对齐

           AD转换后数值是十位的二进制我们用单片机却只是八位的,所以PIC单片机,用两个八位的寄存器来存放AD值,ADRESH用来存放高位结果,ADRESL用来存放低位结果。可是ADRESH和ADRESL加起来是十六啊。那这十位的数值是怎么放在里面的。这就靠左右对齐来设置,

              如果是右对齐 低8八位放在ADRESL,剩下的2位放在ADRESH中。

             如果是左对齐 高8八位放在ADRESH,剩下的2位放在ADRESL中。见下图

            

             我们这里选择右对齐,所以AD控制寄存器1 ADCON1的 ADFM=1

           

              上面将有关ADCON1寄存器的配置说完了。下面来讲解ADCON0

            选择ADC输入通道  

            AD转换模块只有一个,而AD输入通道有8个AN0~AN7.所以不可能同时进行AD转换,那个需要用我们就分配给那个,根据硬件我们将AD转换模块分配给AN0.

              所以 ADCON0 的CHS<4:0>=0000;

            开启ADC模块

             ADC模块开启,ADCON0的ADON=1,只是单纯的启用ADC模块。并不开始AD转换。如果不用ADC模块时候建议关闭。可以省点电哦!!!

            

3 开始AD转换

               ADCON0的GO/DONE=1开启AD转换。

 4 等待AD转换结束


 5 读取结果

     一般情况下我们并不取一次的AD转换的值。而是取多次之后算平均值。这样来确保转换的准确性。 配置ADC模块,有许多地方并没有讲解为什么这么配置,因为许多配置其实是比较随意的。并不是那么的绝对的。一定非要选择哪一个。当然实际的配置还是要根据你项目需求。

//开发环境MPLAB X IDE ,单片机PIC16LF1823. 

#include <pic.h>

__CONFIG(FOSC_INTOSC&WDTE_OFF&PWRTE_ON&MCLRE_OFF&CP_ON&CPD_OFF&BOREN_ON

                   &CLKOUTEN_OFF&IESO_ON&FCMEN_ON);//这个要放到上一行去


__CONFIG(PLLEN_OFF&LVP_OFF) ;
#define  ADC_NUM   8 //转换的次数
#define  LED       LATA1
void init_GPIO(void)
{
    TRISA =  0x01;//端口设置为输入
    ANSELA = 0x01;//设置为模拟输入
    PORTA = 0x00;
    LATA  = 0x00;
}
void init_fosc(void)
{
    OSCCON = 0xF0;//32MHZ
}
void init_AD(void)
{
 ADCON1= 0xA0;//右对齐,AD时钟为Fosc/32,参考电压为电源电压,
 ADCON0= 0x00;//选择通道AN0
 ADCON0bits.ADON = 1;//开启模块
}
unsigned int ADC_BAT_ONE(void)//转换一次
{
    unsigned int value;
    value=0;
    ADCON0bits.CHS =0;//选择通道AN0
    ADCON0bits.ADGO=1;//开始转换
    while(ADCON0bits.GO==1);//等待转换结束


    value=(unsigned int)ADRESH;//强制类型转换,因为ADRESH是字符型的只能表示8位二进制。所以必须转换成可以容纳10位二进制的整型。
    value= value<<8;// 将高两位左移8位
    value += ADRESL;//低八位加入ADRESL的值。
    return value;
}
unsigned int ADC_BAT_contiue(void)
{
    unsigned int ADV_MCU[ADC_NUM],ADV_CNT,ADV_ALL;
    ADV_ALL=0;
    for(ADV_CNT=0;ADV_CNT<ADC_NUM;ADV_CNT++)//进行多次AD转换
    {
     ADV_MCU[ADV_CNT]=ADC_BAT_ONE();
    }
     for(ADV_CNT=0;ADV_CNT<ADC_NUM;ADV_CNT++)//计算多次AD转换的平均值
    {
        ADV_ALL += ADV_MCU[ADV_CNT];
    }
    ADV_ALL= ADV_ALL/ADC_NUM;
    return ADV_ALL;//得到结果返回
}
/*
 *
 */
int main(int argc, char** argv) {
     init_fosc();//设置时钟
     init_GPIO();//设置I/O口
     init_AD();//设置AD
     while(1)
     {
         if( ADC_BAT_contiue()>400)//判断输入电压是否大于1.2V
         {
             LED=1;//灯亮
         }
         else
         {
             LED=0;//灯灭
         }


     }
}



PIC单片机-AD转换

阅读数 1539

AD 转换

阅读数 412

单片机DA AD转换

阅读数 38

没有更多推荐了,返回首页