单片机输出模拟电压

2016-01-25 12:01:45 baidu_33836580 阅读数 3924

以前,做而论道写过一篇关于 DAC0832 的博文,网址链接是:


http://hi.baidu.com/do_sermon/item/7935ea093ec1c617addc7098


当时,为了直观的说明 DAC0832 的基本功能,就使用了拨动开关来输入数字量。


后来,时常有人留言或私信,提出怎样利用单片机来驱动 DAC0832 输出周期电压波形、以及如何编程的问题。


其实,用单片机输出数字量,还真是很简单方便的。


电路,也不用像课本里面画的那么复杂,用任意一个单片机的并行口连接到 DAC0832 的数据输入口即可。


用 P1 口,驱动 0832 的电路如下:


利用 DAC0832 输出模拟电压 - 非著名博主 - 电子信息角落


图片链接:http://xiangce.baidu.com/picture/detail/6b6fcb9227f2bf7890e03718dd1dca4037e1b783


按照这个电路,写入、执行下列的小程序,可以看到输出的锯齿波电压的波形图如下所示:


    ORG   0000H

    JMP   START

START:

;------------------------------------------

    MOV   R2,  #0

LOOP:

    MOV   P1,  R2      ;输出

    CALL  DELAY        ;延时

    INC   R2

    SJMP  LOOP

;---------------

DELAY:                 ;延时子程序

    MOV   R6,  #2;1;127

DEL1:

    MOV   R7,  #200

DEL2:

    DJNZ  R7,  DEL2    ;内循环200遍

    DJNZ  R6,  DEL1    ;外循环127遍

    RET

;------------------------------------------

END


利用 DAC0832 输出模拟电压 - 非著名博主 - 电子信息角落


看波形,还是稍微有些失真呵。


同样还是这个电路,执行下列的程序,看看,会出现什么波形?


    ORG   0000H

    JMP   START

START:

;------------------------------------------

SINE:

    MOV   R1, #00H

    MOV   DPTR, #SINTAB

LOOP:

    MOV   A, R1

    MOVC  A, @A + DPTR  ;取数据

    MOV   P1, A         ;输出,D/A 转换

    INC   R1

    CALL  DELAY         ;延时

    SJMP  LOOP

;---------------

SINTAB:                 ;正弦波数据表

    DB  07FH, 083H, 086H, 089H, 08CH, 08FH, 092H, 095H

    DB  098H, 09BH, 09EH, 0A1H, 0A4H, 0A7H, 0AAH, 0ADH

    DB  0B0H, 0B3H, 0B6H, 0B9H, 0BBH, 0BEH, 0C1H, 0C3H

    DB  0C6H, 0C9H, 0CBH, 0CEH, 0D0H, 0D2H, 0D5H, 0D7H

    DB  0D9H, 0DBH, 0DEH, 0E0H, 0E2H, 0E4H, 0E5H, 0E7H

    DB  0E9H, 0EBH, 0ECH, 0EEH, 0EFH, 0F1H, 0F2H, 0F4H

    DB  0F5H, 0F6H, 0F7H, 0F8H, 0F9H, 0FAH, 0FBH, 0FBH

    DB  0FCH, 0FDH, 0FDH, 0FDH, 0FEH, 0FEH, 0FEH, 0FEH

    DB  0FFH, 0FEH, 0FEH, 0FEH, 0FEH, 0FDH, 0FDH, 0FDH

    DB  0FCH, 0FBH, 0FBH, 0FAH, 0F9H, 0F8H, 0F7H, 0F6H

    DB  0F5H, 0F4H, 0F2H, 0F1H, 0EFH, 0EEH, 0ECH, 0EBH

    DB  0E9H, 0E7H, 0E5H, 0E4H, 0E2H, 0E0H, 0DEH, 0DBH

    DB  0D9H, 0D7H, 0D5H, 0D2H, 0D0H, 0CEH, 0CBH, 0C9H

    DB  0C6H, 0C3H, 0C1H, 0BEH, 0BBH, 0B9H, 0B6H, 0B3H

    DB  0B0H, 0ADH, 0AAH, 0A7H, 0A4H, 0A1H, 09EH, 09BH

    DB  098H, 095H, 092H, 08FH, 08CH, 089H, 086H, 083H

    DB  7FH, 7CH, 79H, 76H, 73H, 70H, 6DH, 6AH

    DB  67H, 64H, 61H, 5EH, 5BH, 58H, 55H, 52H

    DB  4FH, 4CH, 49H, 46H, 44H, 41H, 3EH, 3BH

    DB  39H, 36H, 34H, 31H, 2FH, 2CH, 2AH, 28H

    DB  26H, 23H, 21H, 1FH, 1DH, 1BH, 19H, 18H

    DB  16H, 14H, 12H, 11H, 0FH, 0EH, 0DH, 0BH

    DB  0AH, 09H, 08H, 07H, 06H, 05H, 04H, 03H

    DB  03H, 02H, 02H, 01H, 01H, 01H, 01H, 00H

    DB  00H, 00H, 01H, 01H, 01H, 01H, 02H, 02H

    DB  03H, 03H, 04H, 05H, 06H, 07H, 08H, 09H

    DB  0AH, 0BH, 0DH, 0EH, 0FH, 11H, 12H, 14H

    DB  16H, 18H, 19H, 1BH, 1DH, 1FH, 21H, 23H

    DB  26H, 28H, 2AH, 2CH, 2FH, 31H, 34H, 36H

    DB  39H, 3BH, 3EH, 41H, 44H, 46H, 49H, 4CH

    DB  4FH, 52H, 55H, 58H, 5BH, 5EH, 61H, 64H

    DB  67H, 6AH, 6DH, 70H, 73H, 76H, 79H, 7CH

;---------------

DELAY:                  ;延时子程序

    MOV   R6,   #1;127

DEL1:

    MOV   R7,   #200

DEL2:

    DJNZ  R7,   DEL2    ;内循环200遍

    DJNZ  R6,   DEL1    ;外循环127遍

    RET

;------------------------------------------

END


利用 DAC0832 输出模拟电压 - 非著名博主 - 电子信息角落


确实是个正弦波。


想要改变波形的形状,修改程序中的数据表内容即可。


想要改变波形的周期,修改程序中的延时子程序即可。



2018-06-02 11:42:44 nanfeibuyi 阅读数 16318

单片机 数字电压表(ADC0809)

一、简述

    采用模数转换的芯片ADC0809实现设计数字电压表。例子中设计的数字电压表可以测量0~5V范围内的输入电压值,并且通过4位LED数码管显示采集的电压值,例子测量三个模拟值:4.995、2.5、0.005。

    ADC0809是可以将我们要测量的模拟电压信号量转换为数字量从而可以进行存储或显示的一种转换器件。

   文件打包:链接: https://pan.baidu.com/s/1F2E135Jw7TG3EmUVx3xKGg 提取码: 5ttz

二、效果

三、工程文件

1、Keil工程

2、仿真电路图

四、源文件

#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar code led[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//数码管显示0-9的电平码
uint volt,vtime; //电压值测定值 
uchar addr;//测量地址位
sbit LW1=P2^3; //对应第4个数码管
sbit LW2=P2^2; //对应第3个数码管
sbit LW3=P2^1; //对应第2个数码管
sbit LW4=P2^0; //对应第1个数码管
sbit LW5=P1^4;//指示当前显示的是第几个转换值
sbit CLK=P2^4; //时钟信号
sbit START=P2^5; //转换启动开关
sbit EOC=P2^6;	//转换结束标志
sbit OE=P2^7;//定义ADC0809各脚
/**********************************************************/
//函数名:delay(uint x)
//功能:延时程序 改变测量地址 	
//调用函数:
//输入参数:x
//输出参数:
//说明:程序的延时时间为x乘以0.5ms 每5s改变测量地址位
/**********************************************************/
void delay(uint x)
{
	uchar y,z;
	for(y=x;y>0;y--)
		for(z=250;z>0;z--);//该步运行时间约为0.5ms
		vtime++;
		if(vtime==1000)
		{
			vtime=0;
			addr++;
			if(addr==3)	 //本例子一共有3个测量输入值,轮流读取这3个值并转换显示
				addr=0;//以上语句实现测量地址位的改变		
		}	
}
/**********************************************************/
//函数名:ADC()
//功能:数模转换程序
//调用函数:
//输入参数:
//输出参数:
//说明:将转换好的测定值保存在变量volt中
/**********************************************************/
void ADC()
{
	EA=1;//开中断

	//确保进入正常AD转换状态?
	START=0;
	START=1;

	START=0;//ad开始转换		
	while(EOC==0); //等待转换结束
	OE=1; //输出数据标志为真
	
	EA=0;//关中断
	volt=P3;//获取转换值保存到volt中,(P3为转换后数据)
	volt=volt*196;//转换值处理 (例子的满量程为5V,转换分辩率为8位即最大值是255,5/255=196mV,即1代表196mV)
	
	OE=0;//输出转换结束 (要在获取转换值之后)
}
/**********************************************************/
//函数名:display()
//功能:4位数码管显示
//调用函数:delay(uint x)
//输入参数:
//输出参数:
//说明:将处理后的电压值显示在4位数码管上
/**********************************************************/
void display()
{
	P0=0xff;//消隐 (相当于全部灭灯,清除上次显示效果)
	LW1=0;
	P0=~led[volt/10000]&0x7f;//带小数点1伏显示位
	delay(2);
	P0=0xff;
	LW1=1;
	LW2=0;
	P0=~led[(volt/1000)%10];//100毫伏显示位
	delay(2);
	P0=0xff;
	LW2=1; 
	LW3=0;
	P0=~led[(volt/100)%10];//10毫伏显示位
	delay(2);
	P0=0xff;
	LW3=1;
	LW4=0;
	P0=~led[(volt/10)%10];//1毫伏显示位
	delay(2);
	P0=0xff;
	LW4=1;
	LW5=0;
	P0=~led[addr+1];//显示电压测量位
	delay(2);
	LW5=1;//指示当前显示的是第几个转换值								
}
/**********************************************************/
//主程序
/**********************************************************/ 
void main()
{
	EA=1;//开总中断		   
    TMOD=0x01;//设定定时计数工作方式
	//为定时器初赋值
	TH0=0XFF;
    TL0=0XB4;

	ET0=1;//开定时器0中断 
	TR0=1;//开启定时器0
	while(1)
	{
		P1=addr;//装入测量地址
		LW5=1;
		ADC();//调用模数转换程序
		display();//调用显示程序
	}
}
/**********************************************************/
//函数名:timer() interrupt 1
//功能:定时中断0响应程序
//调用函数:
//输入参数:
//输出参数:
//说明:为ADC提供时钟信号
/**********************************************************/
void timer() interrupt 1
{
	//初值重置
	TH0=0XFF;
    TL0=0XF0;
	CLK=~CLK;//取反 产生时钟信号
}

五、总结

    ADC0809是一种有8路模拟输入、8位并行数字输出的逐次逼近式A/D器件。

1、主要技术指标和特性
    (1) 分辨率:8位;
    (2) 转换时间:取决于芯片的时钟频率,转换1次所需时间;
    (3) 单一电源:+5V;

    (4) 模拟输入电压范围:单级性为0~+5V。

2、引脚介绍

管脚功能说明:
IN0-IN7:模拟量输入通道。就是说它可以分时地分别对八个模拟量进行测量转换。
ADDA-C:地址线。也就是通过这三根地址线的不同编码来选择对哪个模拟量进行测量转换。
ALE:地址锁存允许信号。在低电平时向ADDA-C写地址,当ALE跳至高电平后ADDA-C上的数据被锁存
START:启动转换信号。当它为上升沿后,将内部寄存器清0。当它为下降沿后,开始A/D转换。
D0-D7:数据输出口。转换后的数字数据量就是从这输出给S52的。
OE:输出允许信号,是对D0-D7的输出控制端,OE=0,输出端呈高阻态,OE=1,输出转换得到的数据。
CLOCK:时种信号。ADC0809内部没有时钟电路,需由外部提供时钟脉冲信号。
EOC:转换结束状态信号。EOC=0,正在进行转换。EOC=1,转换结束,可以进行下一步输出操作

Vref(+)、Vref(-):参考电压。参考电压用来与输入的模拟量进行比较,作为测量的基准。一般Vref(+)=5v ,Vref(-)=0V。

3、时序图与工作过程

    时序图:

工作过程:

 ①在IN0-IN7上可分别接上要测量转换的8路模拟量信号。可只接一路。
 ②将ADDA-ADDC端给上代表选择测量通道的代码。如000(B)则代表通道0;001(B)代表通道1;111则代表通道7。
 ③将ALE由低电平置为高电平,从而将ADDA-ADDC送进的通道代码锁存,经译码后被选中的通道的模拟量送给内部转换单元。
 ④给START一个正脉冲。当上升沿时,所有内部寄存器清零。下降沿时,开始进行A/D转换;在转换期间,START保持低电平。
 ⑤EOC为转换结束信号。在上述的A/D转换期间,可以对EOC进行不断测量,当EOC为高电平时,表明转换工作结束。否则,表明正在进行A/D转换。
 ⑥当A/D转换结束后,将OE设置为1,这时D0-D7的数据便可以读取了。OE=0,D0-D7输出端为高阻态,OE=1,D0-D7端输出转换的数据。
    说明:ADC0809的转换工作是在时钟脉冲的条件下完成的,因此首先要在CLOCK端给它一个时钟信号,说明书上给出了可以接入的脉冲信号频率是在10KHz-1280KHz,典型值是640KHz。我们这里取值50KHz。
  时序图上的teoc时长为:从START上升沿开始后的8个时钟同期再加2微秒。这一点得注意,因为当START脉冲刚结束进入转换工作时,EOC还没有立即变为低电平而是过了8个时钟周期后才进入低电平的,所以再给出START脉冲后最好延时一会再进行EOC的检测。

  一个通道的转换时间一般为64个时钟周期,如时钟频率为640KHz时,时钟周期为1.5625微秒,一个通道的转换时间则为1.5625×64=100微秒,那么1秒种就可以转换1000000÷100=10000次。

4、计算定时器初值:

    信号频率取值50KHz,晶振频率是12MHz。

    12MHz时钟频率下,12MHz = 12000KHz = 12000000Hz,一个时钟振荡周期就是1/12000000秒,亦即1/12微秒,标准MCS51系列单片机的一个机器周期是12个时钟振荡周期,也就是12 * (1/12) = 1微秒。

要输出50KHz的矩形方波 周期 T = (1/50K)s 等宽 也就是占空比50% 高电平时间是(0.5/50K)s = 0.01 ms = 10us

 

然而,实际上仿真时这个频率是太快了(不知道是否是计算方法错了),编程中没有使用该初值。

六、Proteus相关操作

      1、电压探针元件(显示电压)

           

   2、自定义电源电压

        

-------------------------------------------------以下是回复 Rainynightsunset 这位兄弟---------------------------

下载打包文件之后:

========================以下回复  木子Jasmine 这位兄弟======================

手动复位效果 (使用轮询方式检测按键有没有按下,因为外部中断引脚被使用了)

测试代码以及仿真文件:链接: https://pan.baidu.com/s/1zC8uhzlbIFRBOkOWuWzAWQ 提取码: wr39

 

 

===================以下回复 Leon. 这位兄弟=======================

将程序中的addr最值修改为8,如下:

在仿真上,添加5个模拟输入

效果:(注:想要查看电压,请加上电压探针元件)

 

 

============== ===== 以下回复    馨心愢忞   ========================

简单的测量20v(采用分压法:先把电压除以4再进行转换,得到转换的值后再乘以4)

测试例子:链接: https://pan.baidu.com/s/10RzT9lFVbB8db4CO7at_0A 提取码: zwem

测试效果:

分压测量20V说明:

 

=======================以下回复 yikai0511validatetoken==============================

以下例子是:按键设置最高电压,当前电压大于等于最高电压时蜂鸣器报警以及LED亮,测试环境为Win10

电压为20V的可以自己设置的,上下限值同时设置暂时没弄好(感觉引脚不够用,需要复用引脚)

例子打包:链接: https://pan.baidu.com/s/1RY18AeWCCsXXqlgUBeTv_w 提取码: pdf7

仿真截图:

 

效果:

 

2018-11-13 20:10:34 Beking17113 阅读数 3075

  最近应实验室需求写一篇51单片机产生PWM的文章供参考.

  单片机芯片STC89C52,晶振12MHz,编译环境Keil5.

  分析:若使单片机端口为高电平, 则单片机输出电压为恒值5V. 反之低电平输出电压为0V. 但是我们如果在周期为T的时间内使单片机高电平出现t1秒, 低电平出现t2秒, 那么在周期内, 高电平出现的时间为, 低电平时间为. 由于周期T的时间极短, 那么在连续的时间t内, 相应端口输出电压为.

  实验使用定时器0, 定时器0由两个寄存器进行控制,分别为定时器/计数器工作方式寄存器TMOD,  定时器/计数器控制寄存器TCON.

TMOD是一个八位寄存器,如下:

,

TMOD寄存器高4位控制定时器1低四位控制定时器0: 

1. GATE位为门控制位, 选择是否让外部中断控制定时器,此位一般置0, 即不让外部中断控制定时器.

2. C/T位为选择定时器/计数器模式选择, 此实验中使用定时器模拟时间周期T,  所以置0选择定时器模式.

3. M1和M0为定时器工作方式选择位, 控制方式如下:

 

一般在周期短,要求定时精度高的情况下选择方式2,但此实验选择方式1的16位计数器.

16位计数器的意思是周期T的长度不超过机器周期的倍,在12MHz晶振的作用下,机器周期为1us,故周期T不超过65536us.

 

TCON控制寄存器寄存器的高4位用于控制定时/计数器的启动和中断申请, 低4位用于控制外部中断(此处不介绍).

  1. TF1:定时器1溢出中断请求标志位。定时器1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。定时器1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
  2. TR1:定时器1运行控制位。TR1置1时,定时器1开始工作;TR1置0时,定时器1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
  3. TF0:定时器0溢出中断请求标志位,其功能与TF1类同。
  4. TR0:定时器0运行控制位,其功能与TR1类同。

 

定时器工作方式: (以工作方式1为模板)

  方式1为16位计数,由定时器的低8位 和 定时器的高8位组成。定时器的低8位溢出时向定时器的高8位进位+1,定时器的高8位溢出时,置位TCON中的TF0标志,然后定时器高8为与低8位请零, 并向CPU发出中断请求.

TL0->TH0->TF0->产生中断(TL0/TH0/TF0清零)

下面介绍个定时器0实现PWM控制LED亮度的代码:

利用定时器0产生周期为100*100us, 电平持续时间为100*20us的方波. 

方波如下:

因为这里我们是产生周期为10ms(100Hz)的PWM,所以可设置中断的时间基准为100us, 中断100次即为10ms. 并在中断里设置时间变量为n.

 1、当n>20时(0< n <T),让单片相应的端口输出低电平;(LED灯亮)

 2、当20<n<=T时,让单片相应的端口输出高电平,此时PWM中低电平的占空比就为%n。

 3、当n=100时,表明周期T已满,n清零;

 下面程序产生20%占空比的pwm:

#include <reg52.h>

#define T 100     //定义时间进度为100个机器周期,即100us

sbit LED=P1^0;

unsigned int pwm_num=20;     //PWM中低电平出现的时间t2

void main()
{
	TMOD = 0x01;            //选择为定时器0 工作方式1
	TH0=(65536-T)/256;      //定时器高八位
	TL0=(65536-T)%256;      //定时器低八位
	TR0=1;                  //开启定时器
	ET0=1;                  //开启定时器1的中断使能
	EA=1;                   //开启总中断
	while (1);              //等待中断发生
}

void Timer1() interrupt 1   //中断1(定时器0专用中断通道)
{
	static unsigned char n; //静态变量n  可以看成每100us加一次的时间t
	TH0=(65536-T)/256;      //重装定时器的值
	TL0=(65536-T)%256;
	n++;                    //表明周期T已到100*100us
	if (n==100)
		n=0;
	LED = (n<=pwm_num) ? 0 : 1;   //判断时间是否超过t2
}

最终实验现象为:LED灯亮度变低.

 

若觉文章有助于你,可以在下面留言点赞哦 (<_>)!

如觉文章何处不明也可留言.

若转载本文请注明出处.

 

2020-02-17 17:41:32 BearPi 阅读数 399

本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的DAC外设,输出任意指定电压值。

1. 准备工作

硬件准备

mark

  • 万用表

软件准备

  • 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;

Keil MDK和串口助手的安装包都可以关注“小熊派开源社区”微信公众号,在资料教程一栏中可获取安装包。

2.生成MDK工程

选择芯片型号

打开STM32CubeMX,打开MCU选择器:

搜索并选中芯片STM32L431RCT6:

配置时钟源

  • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
  • 如果使用默认内部时钟(HSI),这一步可以略过;

这里我都使用外部时钟:

配置串口

小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下:

这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。

接下来开始配置USART1

配置DAC

确定DAC输出通道

查看小熊派E53接口的原理图:

配置DAC

选择DAC1,开启输出通道2,配置保持默认即可:

配置时钟树

STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:

生成工程设置

代码生成设置

最后设置生成独立的初始化文件:

生成代码

点击GENERATE CODE即可生成MDK-V5工程:

3. 在MDK中编写、编译、下载用户代码

重定向printf( )函数

参考:重定向printf函数到串口输出的多种方法

编写测试代码

首先设置DAC输出的数据为12位右对齐,然后指定输出的值0-4096,实际输出的电压为
value/4096x3.3V,最后使能DAC转换,代码如下:

int main(void)
{
    /* USER CODE BEGIN 1 */
    uint16_t i = 0;
    /* USER CODE END 1 */

    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DAC1_Init();
    MX_USART1_UART_Init();

    /* USER CODE BEGIN 2 */
    printf("DAC Test...\r\n");
    HAL_DAC_Start(&hdac1, DAC_CHANNEL_2);

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
    /* USER CODE END WHILE */
        for(i = 0; i < 4096; i++)
        {
            HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, i);
            HAL_Delay(2);
        }
        printf("DAC test finish, test again!\r\n");

    /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}

至此,我们已经学会如何使用DAC输出任意指定电压值