单片机课程设计报告 - 图文

更新时间:2024-06-07 17:28:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

单片机课程设计报告

专 业:

班 级:

学 号:

姓 名:

2014年6月22日

1

目 录

? 数码管消隐……………………………………………………………………….3

1.1项目简介……………………………………………………………………..3 1.2电路原理图………………………………………………………………….4 1.3参考程序……………………………………………………………………..6

? 按键从右至左输入………………………………………………..8

2.1项目简介……………………………………………………………………..8 2.2电路原理图………………………………………………………………10 2.3参考程序………………………………………………………………….10

? 步进电机转速数码管显示………………………………….16

3.1项目简介………………………………………………………………….16 3.2电路原理图………………………………………………………………18 3.3参考程序…………………………………………………………………..19

? PCF8591 DA输出模拟………………………………………….24

4.1项目简介……………………………………………………………………24 4.2电路原理图………………………………………………………………..25 4.3参考程序…………………………………………………………………….26

? 自动调光测试……………………………………………………..30

5.1项目简介……………………………………………………………………30 5.2电路原理图………………………………………………………………..31 5.3参考程序……………………………………………………………………32

2

数码管消隐

1.1项目简介

LED显示器有共阳极和共阴极两种,共阴极LED显示器是发光二极管的阴极连在一起,通常此公共阴极接地;共阳极LED显示器是发光二极管的阳极连在一起,公共阳极接正电压。通过LED显示器中二极管的亮灭,则显示不同的字符或数字。

数码管外形图如下, 共阴极显示如“HELLO”的数据代码如下。 D7 D6 D5 D4 D3 D2 D1 D0 显数值h g f e d c b a 示 代码 0 0 0 0 0 0 1 1 0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 0 0 1 1 1 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 1 0 H E L L O - 76H 79H 38H 38H 3FH 40H

共阴极发光二极管内部结构图

多位数码管的动态显示

在多位8段数码管显示时,为了简化硬件电路,通常将所有位的段选线相应地并联在一起,由一个单片机的8位I/O口控制,形成段选线的多路复用。而各位数码管的共阳极或共阴极分别由单片机独立的I/O口线控制,顺序循环地点亮每位数码管,这样的数码管驱动方式就称为“动态扫描”。在这种方式中,虽然每一时刻只选通一位数码管,但由于人眼具有一定的“视觉残留”,只要延时时间设置恰当,便会感觉到多位数码管同时被点亮了。 多位8段LED动态显示器电路,其中段选线占用一个8位I/O口,位选线占用一个8位I/O口,由于各位的段选线并联,段线码的输出对各位来说都是相同的。因此,同一时刻,如果各位位选线都处于选通状态的话,8位LED将显示相同的字符。若要各位LED能够显示出与本位相应的显示字符,就必须采用扫描显示方式,即在某一位的位选线处于选通状态时,其它各位的位选线处于关闭状态,这样,8位LED中只有选通的那一位显示出字符,而其它位则是熄灭的。同样,在下一时刻,只让下一位的位选线处于选通状态,而其他的位选线处于关闭状态。如此循环下去,就可以使各位“同时”显示出将要显示的字符。由于人眼有视觉暂留现象,只要每位显示间隔足够短,则可造成多位同时亮的假象,达到显示的目的。

3

多位数码管分别显示不同数字,这种扫描显示方式成为动态扫描,并不停变化赋值当高位值为0是不显示该位,即消隐。如数字 0010,实际显示为10,前2位0不显示。

如果不消隐 ,一位显示完后,显示下一位时,下一位的字形码也会加到这位,造成乱码。原因:CPU的执行速度很快,当送入位选和段选数据后,接着又送入位选数据,但该位的段选数据还没有送入,所以该位还保持着上次的段选数据,接着该位的段选数据送入,由于视觉残留,两个段选数据的显示效果重合,形成了混乱。

1.2电路原理图

4

5

1.3参考程序

/*----------------------------------------------- 名称:多位数码管动态扫描显示变化数据 数码管消隐

内容:多位数码管分别显示不同数字,这种扫描显示方式成为动态扫描,并不停变化赋值 当高位值为0是不显示该位,即消隐,如数字 0010,实际显示为10,前2位0不显示

------------------------------------------------*/ #include //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义

#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换 sbit LATCH1=P2^2;//定义锁存使能端口 段锁存 sbit LATCH2=P2^3;// 位锁存

unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9

unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码

unsigned char TempData[8]; //存储显示值的全局变量 void Delay(unsigned int t); //函数声明

void Display(unsigned char FirstBit,unsigned char Num); /*------------------------------------------------ 主函数

------------------------------------------------*/ main() {

unsigned int num; unsigned int j; while(1) { j++;

if(j==10) //检测当前数值显示了一小段时间后,需要显示的数值加1,实现数据显示的变化 { j=0; num++;

if(num==10000)//用于显示0~9999 num=0; }

if(num<1000) //如果小于1000则千位不显示

6

TempData[0]=0; else

TempData[0]=dofly_DuanMa[num/1000];//分解显示信息,如要显示68,则68/10=6 68=8

if(num<100) //如果小于100则千位和百位都不显示 TempData[1]=0; else

TempData[1]=dofly_DuanMa[(num00)/100];

if(num<10) //如果小于10,则千位、百位和十位都不显示 TempData[2]=0; else

TempData[2]=dofly_DuanMa[((num00)0)/10]; TempData[3]=dofly_DuanMa[((num00)0)]; Display(2,4); } }

/*------------------------------------------------ 延时函数,含有输入参数 unsigned int t,无返回值 unsigned int 是定义无符号整形变量,其值的范围是 0~65535

------------------------------------------------*/ void Delay(unsigned int t) {

while(--t); }

/*------------------------------------------------ 显示函数,用于动态扫描数码管

输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。

Num表示需要显示的位数,如需要显示99两位数值则该值输入2 ------------------------------------------------*/ void Display(unsigned char FirstBit,unsigned char Num) {

unsigned char i;

for(i=0;i

DataPort=0; //清空数据,防止有交替重影 LATCH1=1; //段锁存 LATCH1=0;

DataPort=dofly_WeiMa[i+FirstBit]; //取位码 LATCH2=1; //位锁存 LATCH2=0;

7

DataPort=TempData[i]; //取显示数据,段码 LATCH1=1; //段锁存 LATCH1=0;

Delay(200); // 扫描间隙延时,时间太长会闪烁,太短会造成重影

} }

按键从右至左输入

2.1项目简介

按键作为一种简单实用的输入设备已经应用于各种单片机应用系统中,可谓是无处不在。但在不同的实用场合下所使用的按键也不尽相同。这里就对几种经常用到的按键及其使用方法进行介绍。

1、传统按键:传统按键是现在在各种电子设备中应用最为广泛的按键,可能它们的形状各有不同,但其控制方法却大同小异,利用按键是否按下的IO电平状态变化来对其进行识别。

2、直接按键:最简单的键盘就是把电平信号直接接到IO上。在程序里面读取IO电平状态,如果读到相应的电平,则说明此IO上所接的按键被按下。这种方法原理与控制方法都非常简单,但造成了IO 资源的浪费。这种按键方式的示意图如下:

8

3、扫描按键矩阵:这种按键输入方式很巧妙地利用了IO资源,使得8个IO可以实现16键键盘。它的示意图如下:

这种按键输入方式比上面的直接按键方式从原理与控制上都比上面的直接按键要复杂。它通过IO的扫描来获取键值,其实扫描过程非常简单,过程如下:KEY1~KEY4对应于IO0~IO3,KEY5~KEY8对应于IO4~IO7。 1.将IO0~IO3置高,将IO4置低,IO5、IO6、IO7置高,读取IO0~IO3。第一列的某个按钮按下后,使相应的两个触点接通,相应行上的IO可以读到低电平。2.将IO0~IO3置高,将IO5置低,IO4、IO6、IO7置高,读取IO0~IO3。第一列的某个按钮按下后,使相应的两个触点接通,相应行上的IO可以读到低电平。3.将IO0~IO3置高,将IO6置低,IO4、IO5、IO7置高,读取IO0~IO3。第一列的某个按钮按下后,使相应的两个触点接通,相应行上的IO可以读到低电平。4.将IO0~IO3置高,将IO7置低,IO4、IO5、IO6置高,读取IO0~IO3。第一列的某个按钮按下后,使相应的两个触点接通,相应行上的IO可以读到低电平。循环此过程可以不断读取按键键值。 在实际的应用中,这个扫描过程通常是放在定时器的中断服务程序中去完成的,通过全局变量将键值返回到其它函数中去。当然,作为学习其扫描过程,也可以将扫描放在主函数中直接来完成。 在上面介绍的按键输入方式中,读者可以看到,IO都是以低电平作为检测电平,那这是为什么呢?这是因为单片机IO读取电平状态的时候,读取低电平比高电平要稳定。也下是因为这一原因外部中断以低电平或下降沿来作为其中断触发条件,以保证中断的可靠性。

识别键的闭合,通常采用行扫描法和行反转法。行扫描法是使键盘上某一行线为低电平,而其余行接高电平,然后读取列值,如读列值中某位为低电平,表明有键按下,否则扫描下一行,直到扫完所有行。

行反转法识别闭合键时,要将行线接一并行口,先让它工作在输出方式,将列线也接到一个并行口,先让它工作于输入方式,程序使CPU通过输出端口在各行线上全部送低电平,然后读入列线值,如此时有某键被按下,则必定会使某一列线值为0。然后,程序对两个并行端口进行方式设置,使行线工作于输入方式,列线工作于输出方式,并将刚才读得的列线值从列线所接的并行端口输出,再读取行线上输入值,那么,在闭合键所在行线上的值必定为0。这样,当一个键被接下时,必定可以读得一对唯一的行线值和列线值。

9

由于51单片机的并口能够动态地改变输入输出方式,因此,矩阵键盘采用行反转法识别最为简便。 行反转法识别按键的过程是:首先,将4个行线作为输出,将其全部置0,4个列线作为输入,将其全部置1,也就是向P1口写入0xF0;假如此时没有人按键,从P1口读出的值应仍为0xF0;假如此时1、4、7、0四个键中有一个键被按下,则P1.6被拉低,从P1口读出的值为0xB0;为了确定是这四个键中哪一个被按下,可将刚才从P1口读出的数的低四位置1后再写入P1口,即将0xBF写入P1口,使P1.6为低,其余均为高,若此时被按下的键是“4”,则P1.1被拉低,从P1口读出的值为0xBE;这样,当只有一个键被按下时,每一个键只有唯一的反转码,事先为12个键的反转码建一个表,通过查表就可知道是哪个键被按下了。

本项目要求单个独立按键依次输入控制,按键从右至左输入,如计算器输入数据形式相同从右至左。

2.2电路原理图

键盘连接成4×4的矩阵形式,占用单片机P1口的8根线,行信号是P1.0-1.3,列信号是P1.4-1.7。

2.3参考程序

/*----------------------------------------------- 名称:单个独立按键依次输入控制

内容:如计算器输入数据形式相同 从右至左

------------------------------------------------*/

#include //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义

10

sbit KEY1=P3^0; //定义按键输入端口 sbit KEY2=P3^1; sbit KEY3=P3^2; sbit KEY4=P3^3; sbit KEY5=P3^4; sbit KEY6=P3^5; sbit KEY7=P3^6; sbit KEY8=P3^7;

#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换 sbit LATCH1=P2^2;//定义锁存使能端口 段锁存 sbit LATCH2=P2^3;// 位锁存

unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9

unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码

unsigned char TempData[8]; //存储显示值的全局变量

void DelayUs2x(unsigned char t);//us级延时函数声明 void DelayMs(unsigned char t); //ms级延时

void Display(unsigned char FirstBit,unsigned char Num);//数码管显示函数 unsigned char KeyScan(void);//键盘扫描 void Init_Timer0(void);//定时器初始化

/*------------------------------------------------ 主函数

------------------------------------------------*/ void main (void) {

unsigned char num,i,j; unsigned char temp[8]; Init_Timer0();

while (1) //主循环 {

num=KeyScan(); if(num) {

if(i<8) {

temp[i]=dofly_DuanMa[num];

11

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

TempData[7-i+j]=temp[j]; } i++;

if(i==9)//多出一个按键输入为了清屏 原本应该为8 { i=0;

for(j=0;j<8;j++)//清屏 TempData[j]=0; } } } }

/*------------------------------------------------ uS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编,大致延时 长度如下 T=tx2+5 uS

------------------------------------------------*/ void DelayUs2x(unsigned char t) {

while(--t); }

/*------------------------------------------------ mS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编

------------------------------------------------*/ void DelayMs(unsigned char t) {

while(t--) {

//大致延时1mS DelayUs2x(245); DelayUs2x(245); } }

/*------------------------------------------------ 显示函数,用于动态扫描数码管

输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。

Num表示需要显示的位数,如需要显示99两位数值则该值输入2 ------------------------------------------------*/

12

void Display(unsigned char FirstBit,unsigned char Num) {

static unsigned char i=0;

DataPort=0; //清空数据,防止有交替重影 LATCH1=1; //段锁存 LATCH1=0;

DataPort=dofly_WeiMa[i+FirstBit]; //取位码 LATCH2=1; //位锁存 LATCH2=0;

DataPort=TempData[i]; //取显示数据,段码 LATCH1=1; //段锁存 LATCH1=0; i++;

if(i==Num) i=0; }

/*------------------------------------------------ 定时器初始化子程序

------------------------------------------------*/ void Init_Timer0(void) {

TMOD |= 0x01; //使用模式1,16位定时器,使用\符号可以在使用多个定时器时不受影响

//TH0=0x00; //给定初值 //TL0=0x00;

EA=1; //总中断打开 ET0=1; //定时器中断打开 TR0=1; //定时器开关打开 }

/*------------------------------------------------ 定时器中断子程序

------------------------------------------------*/ void Timer0_isr(void) interrupt 1 {

TH0=(65536-2000)/256; //重新赋值 2ms TL0=(65536-2000)%6;

13

Display(0,8); // 调用数码管扫描 }

/*------------------------------------------------ 按键扫描函数,返回扫描键值

------------------------------------------------*/ unsigned char KeyScan(void) {

/********************************************************/ if(!KEY1) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY1) //再次确认按键是否按下,没有按下则退出 {

while(!KEY1);//如果确认按下按键等待按键释放,没有则退出 {

return 1; } } }

/********************************************************/ else if(!KEY2) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY2) //再次确认按键是否按下,没有按下则退出 {

while(!KEY2);//如果确认按下按键等待按键释放,没有则退出 {

return 2; } } }

/********************************************************/ else if(!KEY3) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY3) //再次确认按键是否按下,没有按下则退出 {

while(!KEY3);//如果确认按下按键等待按键释放,没有则退出 {

return 3; } }

14

}

/********************************************************/ else if(!KEY4) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY4) //再次确认按键是否按下,没有按下则退出 {

while(!KEY4);//如果确认按下按键等待按键释放,没有则退出 {

return 4; } } }

/********************************************************/ else if(!KEY5) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY5) //再次确认按键是否按下,没有按下则退出 {

while(!KEY5);//如果确认按下按键等待按键释放,没有则退出 {

return 5; } } }

/********************************************************/ else if(!KEY6) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY6) //再次确认按键是否按下,没有按下则退出 {

while(!KEY6);//如果确认按下按键等待按键释放,没有则退出 {

return 6; } } }

/********************************************************/ else if(!KEY7) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY7) //再次确认按键是否按下,没有按下则退出 {

while(!KEY7);//如果确认按下按键等待按键释放,没有则退出

15

{

return 7; } } }

/********************************************************/ else if(!KEY8) //如果检测到低电平,说明按键按下 {

DelayMs(10); //延时去抖,一般10-20ms

if(!KEY8) //再次确认按键是否按下,没有按下则退出 {

while(!KEY8);//如果确认按下按键等待按键释放,没有则退出 {

return 8; } } }

/********************************************************/ else

return 0; }

步进电机转速数码管显示

3.1项目简介

步进电机广泛应用于对精度要求比较高的运动控制系统中,如机器人、打印机、软盘驱动器、绘图仪、机械阀门控制器等。目前,对步进电机的控制主要有由分散器件组成的环形脉冲分配器、软件环形脉冲分配器、专用集成芯片环形脉冲分配器等。分散器件组成的环形脉冲分配器体积比较大,同时由于分散器件的延时,其可靠性大大降低;软件环形分配器要占用主机的运行时间,降低了速度;专用集成芯片环形脉冲分配器集成度高、可靠性好,但其适应性受到限制,同时开发周期长、需求费用较高。步进电机是微特电机的一种,其作为执行元件,是机电一体化的关键产品之一,广泛应用在各种自动化控制系统中。随着微电子和计算机技术的发展,步进电机的需求量与日俱增,在各个国民经济领域都有应用。同时步进电动机是一种将脉冲信号变换成相应的角位移(或线位移)的电磁装置,是一种特殊的电动机。一般电动机都是连续转动的,而步进电动机则有定位和运转两种基本状态,当有脉冲输入肘步进电动机一步一步地转动,每给它一个脉冲信号,它就转过一定的角度。步进电动机的角位移量和输入脉冲的个数严格成正比,在时间上

16

与输入脉冲同步,因此只要控制输入脉冲的数量、频率及电动机绕组通电的相序,便可获得所需的转角、转速及转动方向。在没有脉冲输入时,在绕组电源的激励下气隙磁场能使转子保持原有位置于定位状态。步进电机以广泛应用在生产实践的各个领域。它最大的应用是在数控机床的制造中,因为步进电机不需要A/D转换,能够直接将数字脉冲信号转化成为角位移,所以被认为是理想的数控机床的执行元件。早期的步进电机输出转矩比较小,无法满足需要,在使用中和液压扭矩放大器一同组成液压脉冲马达。随着步进电动机技术的发展,步进电动机已经能够单独在系统上进行使用,成为了不可替代的执行元件。除了在数控机床上的应用,步进电机也可以并用在其他的机械上,比如作为自动送料机中的马达,作为通用的软盘驱动器的马达,也可以应用在打印机和绘图仪中。伴随着不同的数字化技术的发展以及步进电机本身技术的提高,步进电机将会在更多的领域得到应用。

步进电机是机电控制中一种常用的执行机构,它的用途是将电脉冲转化为角位移,它的的驱动电路根据控制信号工作,控制信号由单片机产生。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度,控制换相顺序,即通电控制脉冲必须严格按照一定顺序分别控制各相的通断。通过控制脉冲个数即可以控制角位移量,从而达到正确定位的目的。控制步进电机的转向,即给定工作方式正序换相通电,步进电机正转,若按反序通电换相,则电机就反转。控制步进电机的速度,即给步进电机发一个控制脉冲,它就转一步,再发一个脉冲,它会再转一步,两个脉冲的间隔越短,步进电机就转得越快。同时通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。

该步进电机为一四相步进电机,采用单极性直流电源供电。只要对步进电机的各相绕组按合适的时序通电,就能使步进电机步进转动。图是该四相反应式步进电机工作原理示意图。

四相步进电机步进示意图

开始时,开关SB接通电源,SA、SC、SD断开,B相磁极和转子0、3号齿对齐,同时,转子的1、4号齿就和C、D相 绕组磁极产生错齿,2、5号齿就和D、A相绕组磁极产生错齿。当开关SC接通电源,SB、SA、SD断开时,由于C相绕组的磁力线和1、4号齿之间磁力线的作用,使转子转动,1、4号齿和C相绕组的磁极对齐。而0、3号齿和A、B相绕组产生错齿,2、5号齿就和A、D相绕组磁极产生错齿。依次类推,A、B、C、D四相绕组轮流供电,则转子会沿着A、B、C、D方向转动。 四相步进电机按照通电顺序的不同,可分为单四拍、双四拍、八拍三种工作方式。单四拍与双四拍的步距角相等,但单四拍的转动力矩小。八拍工作方式的步距角是单四拍与双四拍的一半,因此,八拍工作方式既可以保持较高的转动力矩又可以提高控制精度。 单四拍、双四拍与八拍工作方式的电源通电时序与波形分别如图4.a、b、c所示:

17

a. 单四拍 b. 双四拍 c.八拍

步进电机工作时序波形图

对步进电机四个绕组依次实现如下方式的循环通电控制: 单四拍运行:正转A-B-C-D;反转 D-C-B-A 双四拍运行:正转AB-BC-CD-DA;反转DC-CB-BA-AD 单双八拍运行:正转A-AB-B-BC-C-CD-D-DA

本项目要求步进电机转速数码管显示,测试4相步进电机常规驱动,使用1-2相励磁1-2相激励功率增倍,步进角度减半,抖动减少顺序如:a-ab-b-bc-c-cd-d-da,又称4相8拍 数码管显示 03-19速度等级,数字越大,速度越慢。

3.2电路原理图

18

3.3参考程序

/*----------------------------------------------- 名称:步进电机

内容:本程序用于测试4相步进电机常规驱动 使用1-2相励磁

1-2相激励功率增倍,步进角度减半,抖动减少 顺序如下 a-ab-b-bc-c-cd-d-da 又称4相8拍 数码管显示 03-19速度等级,数字越大,速度越慢

19

------------------------------------------------*/

#include

#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换 sbit LATCH1=P2^2;//定义锁存使能端口 段锁存 sbit LATCH2=P2^3;// 位锁存

unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9

unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码

unsigned char TempData[8]; //存储显示值的全局变量

sbit A1=P1^0; //定义步进电机连接端口 sbit B1=P1^1; sbit C1=P1^2; sbit D1=P1^3;

#define Coil_AB1 {A1=1;B1=1;C1=0;D1=0;}//AB相通电,其他相断电 #define Coil_BC1 {A1=0;B1=1;C1=1;D1=0;}//BC相通电,其他相断电 #define Coil_CD1 {A1=0;B1=0;C1=1;D1=1;}//CD相通电,其他相断电 #define Coil_DA1 {A1=1;B1=0;C1=0;D1=1;}//D相通电,其他相断电 #define Coil_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通电,其他相断电 #define Coil_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通电,其他相断电 #define Coil_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通电,其他相断电 #define Coil_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通电,其他相断电 #define Coil_OFF {A1=0;B1=0;C1=0;D1=0;}//全部断电

unsigned char Speed;

void Display(unsigned char FirstBit,unsigned char Num); void Init_Timer0(void);

/*------------------------------------------------ uS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编,大致延时 长度如下 T=tx2+5 uS

------------------------------------------------*/ void DelayUs2x(unsigned char t) {

while(--t);

20

}

/*------------------------------------------------ mS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编

------------------------------------------------*/ void DelayMs(unsigned char t) {

while(t--) {

//大致延时1mS DelayUs2x(245); DelayUs2x(245); } }

/*------------------------------------------------ 主函数

------------------------------------------------*/ main() {

unsigned int i=512;//旋转一周时间

Init_Timer0();

EA=1; //全局中断开 EX0=1; //外部中断0开 IT0=1; //1表示边沿触发

Speed=3;

TempData[0]=dofly_DuanMa[Speed/10];//分解显示信息,如要显示68, TempData[1]=dofly_DuanMa[Speed];//则68/10=6 68=8 Coil_OFF

while(i--) //正向 { Coil_A1 DelayMs(Speed);

Coil_AB1 //遇到Coil_AB1 用{A1=1;B1=1;C1=0;D1=0;}代替 DelayMs(Speed); //改变这个参数可以调整电机转速 , //数字越小,转速越大,力矩越小 Coil_B1 DelayMs(Speed); Coil_BC1

DelayMs(Speed); Coil_C1

21

DelayMs(Speed); Coil_CD1

DelayMs(Speed); Coil_D1 DelayMs(Speed); Coil_DA1

DelayMs(Speed); } }

/*------------------------------------------------ 外部中断程序

------------------------------------------------*/ void ISR_INT0(void) interrupt 0 {

if(!INT0) {

DelayMs(10);//在此处可以添加去抖动程序,防止按键抖动造成错误 if(!INT0)

//while(!INT1);//等待按键释放 {

Speed++; if(Speed==20) Speed=3;

TempData[0]=dofly_DuanMa[Speed/10];//分解显示信息,如要显示68, TempData[1]=dofly_DuanMa[Speed];//则68/10=6 68=8 } } }

/*------------------------------------------------ 显示函数,用于动态扫描数码管

输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。

Num表示需要显示的位数,如需要显示99两位数值则该值输入2 ------------------------------------------------*/ void Display(unsigned char FirstBit,unsigned char Num) {

static unsigned char i=0;

DataPort=0; //清空数据,防止有交替重影 LATCH1=1; //段锁存 LATCH1=0;

22

DataPort=dofly_WeiMa[i+FirstBit]; //取位码 LATCH2=1; //位锁存 LATCH2=0;

DataPort=TempData[i]; //取显示数据,段码 LATCH1=1; //段锁存 LATCH1=0; i++;

if(i==Num) i=0; }

/*------------------------------------------------ 定时器初始化子程序

------------------------------------------------*/ void Init_Timer0(void) {

TMOD |= 0x01; //使用模式1,16位定时器,使用\符号可以在使用多个定时器时不受影响

//TH0=0x00; //给定初值 //TL0=0x00;

EA=1; //总中断打开 ET0=1; //定时器中断打开 TR0=1; //定时器开关打开 PT0=1; //优先级打开 }

/*------------------------------------------------ 定时器中断子程序

------------------------------------------------*/ void Timer0_isr(void) interrupt 1 {

TH0=(65536-2000)/256; //重新赋值 2ms TL0=(65536-2000)%6;

Display(0,8); }

23

PCF8591 DA输出模拟

4.1项目简介

PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I2C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。

I2C总线

1、I2C总线数据位的传输

它通过2根线:串行数据线(SDA)和串行时钟线(SCL)组成。连接到总线上的每一个器件都有一个唯一的地址,而且都可以作为一个发生器或接收器,SDA和SCL都是双向线路,分别通过一个电阻连接到电源(+5V)端。前提是连接到总线上的器件的SDA和SCL端必须是漏极或集电极开路型。I2C总线上的数据传输速率在标准模式下可达100Kb/s,快速模式可达400Kb/s,高速模式下可达3.4Mb/s。连接到总线的器件数量只由总线的电容(400PF)限制决定。

I2C总线上每传输一个数据位必须产生一个时钟脉冲,I2C总线上数据传输的有效性要求SDA线上的数据必须在时钟线SCL的高电平期间保存稳定,数据线的改变只能在时钟线为低电平期间。在标准模式下,高低电平宽度必须大于4.7us(即每次时钟线需延时4.7us后才能改变)。 2、I2C总线数据的传输

数据传输的字节格式

发送到SDA线上的每一个字节必须为8位,每次发送的字节数量不受限制,从机在接收完一个字节后向主机发送一个应答位,主机在收到从机应答后才会发送第二字节数据,发送数据时先发数据的最高位。

24

数据传输中的应答

相应的应答位由接收方(从机)产生,在应答的时钟脉冲期间,发送方(主机)应释放SDA线(使其为高电平)。在应答过程中,接收方(从机)必须将数据线SDA拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。 3、I2C总线的传输协议

寻址字节

主机产生起始条件后,发送的第一字节为寻址字节,该字节的前7位为从机地址,最低位决定了传输的方向,该最低位为“0”表示主机写数据到从机,“1”表示主机从从机中读数据。从机地址由一个固定的部分(如高4位1001)和可编程部分(如低3位A0~A2)及一个方向位(R/W)组成。

传输格式

主机产生起始条件后,首先发送一个寻址字节,收到从机应答后,接着就传输数据,数据传输一般由主机产生的停止位终止。但如果主机仍希望在总线上通信,则它可以产生重复起始条件和寻址另一个从机,而不必产生一个停止条件。

本项目使用DA输入,数码管显示输出数字量,LED显示模拟电压大小。 DA转换

发送给PCF8591的第三个字节被存储到DAC数据寄存器,并使用片上DA转换器转换成对应的模拟电压。这个DA转换器由连接至外部的参考电压的具有256个接头的电阻分压电路和选择开关组成。接头译码器切换一个接头至DAC输出线模拟输出电压由自动清零单位增益放大器缓冲。这个缓冲放大器可通过设置控制寄存器的模拟输出容许标志来开户或关闭。在激活状态,输出电压将保持到新的数据字节被发送。片上DA转换器也可用于逐次逼近AD转换.为释放用于AD转换周期的DAC,单位增益放大器还配备了一个跟踪和保持电路。在执行AD转换时该电路保持输出电压。

4.2电路原理图

A/D输入方式选择说明:

25

4.3参考程序

/*----------------------------------------------- 名称:IIC协议 PCF8591 AD/DA转换

内容:使用DA输入,数码管显示输出数字量,LED显示模拟电压大小 ------------------------------------------------*/ #include #include \#include \#include \

#define AddWr 0x90 //写数据地址 #define AddRd 0x91 //读数据地址

extern bit ack;

bit WriteDAC(unsigned char dat);

/*------------------------------------------------ 主程序

------------------------------------------------*/

26

main() {

unsigned char num=0; Init_Timer0();

while (1) //主循环 {

WriteDAC(num);

num++;//连续累加,值从0-255反复循环,并显示在数码管上, TempData[0]=dofly_DuanMa[num/100]; TempData[1]=dofly_DuanMa[(num0)/10]; TempData[2]=dofly_DuanMa[(num0)]; DelayMs(100); } }

/*------------------------------------------------ 写入DA转换数值

输入参数:dat 表示需要转换的DA数值,范围是0-255 ------------------------------------------------*/ bit WriteDAC(unsigned char dat) {

Start_I2c(); //启动总线

SendByte(AddWr); //发送器件地址 if(ack==0)return(0);

SendByte(0x40); //发送器件子地址 if(ack==0)return(0);

SendByte(dat); //发送数据 if(ack==0)return(0); Stop_I2c(); }

#include \

/*------------------------------------------------ uS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编,大致延时 长度如下 T=tx2+5 uS

------------------------------------------------*/ void DelayUs2x(unsigned char t) {

while(--t); }

/*------------------------------------------------ mS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是

27

0~255 这里使用晶振12M,精确延时请使用汇编

------------------------------------------------*/ void DelayMs(unsigned char t) {

while(t--) {

//大致延时1mS DelayUs2x(245); DelayUs2x(245); } }

#include\#include\

#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换 //sbit LATCH1=P2^0;//定义锁存使能端口 段锁存 //sbit LATCH2=P2^3;// 位锁存

unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9

unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码

unsigned char TempData[8]; //存储显示值的全局变量

/*------------------------------------------------ 显示函数,用于动态扫描数码管

输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。

Num表示需要显示的位数,如需要显示99两位数值则该值输入2 ------------------------------------------------*/ void Display(unsigned char FirstBit,unsigned char Num) {

static unsigned char i=0;

DataPort=0; //清空数据,防止有交替重影 LATCH1=1; //段锁存 LATCH1=0;

DataPort=dofly_WeiMa[i+FirstBit]; //取位码 LATCH2=1; //位锁存

28

LATCH2=0;

DataPort=TempData[i]; //取显示数据,段码 LATCH1=1; //段锁存 LATCH1=0; i++;

if(i==Num) i=0; }

/*------------------------------------------------ 定时器初始化子程序

------------------------------------------------*/ void Init_Timer0(void) {

TMOD |= 0x01; //使用模式1,16位定时器,使用\符号可以在使用多个定时器时不受影响

//TH0=0x00; //给定初值 //TL0=0x00;

EA=1; //总中断打开 ET0=1; //定时器中断打开 TR0=1; //定时器开关打开 }

/*------------------------------------------------ 定时器中断子程序

------------------------------------------------*/ void Timer0_isr(void) interrupt 1 {

TH0=(65536-2000)/256; //重新赋值 2ms TL0=(65536-2000)%6;

Display(0,8); }

29

自动调光测试

5.1项目简介

PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I2C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。

PCF8591引脚说明

ANI0~ANI3 为模拟信号输入端,不使用的输入端应接地 A0~A2 地址输入端

GND、VCC 地和电源端(+5V) SDA 为I2C数据输入与输出端 SCL 为I2C时钟输入端

EXT 内外部时钟选择端,使用内部时钟时接地,使用外部时钟时接+5V OSC 外部时钟输入、内部时钟输出端,不使用时应悬空 AGND 模拟信号地 VREF 基准电压输入端 AOUT D/A转换后的电压输出端

30

若集光敏电阻,可以通过AD采集环境光强精确数值使用。本项目要求使用4路AD中的1路检测外部模拟量输入,显示0-255数值DA输出电压用LED显示,主要用于检测环境光线,光线强,则灯亮度小,以保证整体光强稳定在一个范围这种思路和用法主要用于自动调光,可以根据环境亮度自动调节,从而达到节能最大化。

DAC电阻电路

5.2电路原理图

31

5.3参考程序

/*----------------------------------------------- 名称:IIC协议 PCF8591 AD/DA转换 测试光敏电阻

内容:使用4路AD中的1路检测外部模拟量输入,显示0-255数值

DA输出电压用LED显示,主要用于检测环境光线,光线强,则灯亮度小,以保证整体光强稳定在一个范围

这种思路和用法主要用于自动调光,可以根据环境亮度自动调节,从而达到节能最大化,本样例演示用

------------------------------------------------*/ #include #include \#include \#include \

#define AddWr 0x90 //写数据地址 #define AddRd 0x91 //读数据地址

extern bit ack;

32

unsigned char ReadADC(unsigned char Chl); bit WriteDAC(unsigned char dat);

/*------------------------------------------------ 主程序

------------------------------------------------*/ main() {

unsigned char num=0; Init_Timer0();

while (1) //主循环 {

num=256-ReadADC(0);//

TempData[0]=dofly_DuanMa[num/100]; TempData[1]=dofly_DuanMa[(num0)/10]; TempData[2]=dofly_DuanMa[(num0)]; //主循环中添加其他需要一直工作的程序

WriteDAC(num);//把当前值写入DA,用led演示亮度 DelayMs(100); } }

/*------------------------------------------------ 读AD转值程序

输入参数 Chl 表示需要转换的通道,范围从0-3 返回值范围0-255

------------------------------------------------*/ unsigned char ReadADC(unsigned char Chl) {

unsigned char Val;

Start_I2c(); //启动总线

SendByte(AddWr); //发送器件地址 if(ack==0)return(0);

SendByte(0x40|Chl); //发送器件子地址 if(ack==0)return(0); Start_I2c();

SendByte(AddWr+1);

if(ack==0)return(0); Val=RcvByte();

NoAck_I2c(); //发送非应位 Stop_I2c(); //结束总线 return(Val); }

33

/*------------------------------------------------ 写入DA转换数值

输入参数:dat 表示需要转换的DA数值,范围是0-255 ------------------------------------------------*/ bit WriteDAC(unsigned char dat) {

Start_I2c(); //启动总线

SendByte(AddWr); //发送器件地址 if(ack==0)return(0);

SendByte(0x40); //发送器件子地址 if(ack==0)return(0);

SendByte(dat); //发送数据 if(ack==0)return(0); Stop_I2c(); }

#include \

/*------------------------------------------------ uS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编,大致延时 长度如下 T=tx2+5 uS

------------------------------------------------*/ void DelayUs2x(unsigned char t) {

while(--t); }

/*------------------------------------------------ mS延时函数,含有输入参数 unsigned char t,无返回值 unsigned char 是定义无符号字符变量,其值的范围是 0~255 这里使用晶振12M,精确延时请使用汇编

------------------------------------------------*/ void DelayMs(unsigned char t) {

while(t--) {

//大致延时1mS DelayUs2x(245); DelayUs2x(245); } }

#include\#include\

34

#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换 //sbit LATCH1=P2^0;//定义锁存使能端口 段锁存 //sbit LATCH2=P2^3;// 位锁存

unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9

unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码

unsigned char TempData[8]; //存储显示值的全局变量

/*------------------------------------------------ 显示函数,用于动态扫描数码管

输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。

Num表示需要显示的位数,如需要显示99两位数值则该值输入2 ------------------------------------------------*/ void Display(unsigned char FirstBit,unsigned char Num) {

static unsigned char i=0;

DataPort=0; //清空数据,防止有交替重影 LATCH1=1; //段锁存 LATCH1=0;

DataPort=dofly_WeiMa[i+FirstBit]; //取位码 LATCH2=1; //位锁存 LATCH2=0;

DataPort=TempData[i]; //取显示数据,段码 LATCH1=1; //段锁存 LATCH1=0; i++;

if(i==Num) i=0; }

/*------------------------------------------------ 定时器初始化子程序

------------------------------------------------*/

35

void Init_Timer0(void) {

TMOD |= 0x01; //使用模式1,16位定时器,使用\符号可以在使用多个定时器时不受影响

//TH0=0x00; //给定初值 //TL0=0x00;

EA=1; //总中断打开 ET0=1; //定时器中断打开 TR0=1; //定时器开关打开 }

/*------------------------------------------------ 定时器中断子程序

------------------------------------------------*/ void Timer0_isr(void) interrupt 1 {

TH0=(65536-2000)/256; //重新赋值 2ms TL0=(65536-2000)%6;

Display(0,8); }

36

END

void Init_Timer0(void) {

TMOD |= 0x01; //使用模式1,16位定时器,使用\符号可以在使用多个定时器时不受影响

//TH0=0x00; //给定初值 //TL0=0x00;

EA=1; //总中断打开 ET0=1; //定时器中断打开 TR0=1; //定时器开关打开 }

/*------------------------------------------------ 定时器中断子程序

------------------------------------------------*/ void Timer0_isr(void) interrupt 1 {

TH0=(65536-2000)/256; //重新赋值 2ms TL0=(65536-2000)%6;

Display(0,8); }

36

END

本文来源:https://www.bwwdw.com/article/exv6.html

Top