找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10767|回复: 12
打印 上一主题 下一主题
收起左侧

基于51单片机产生占空比和频率可调的方波信号发生器(附全部代码)

  [复制链接]
跳转到指定楼层
楼主
       前段时间开始接触到单片机,以为单片机会很简单,但比我预想的要复杂一些,特别是刚开始接触的时候,需要下载各种软件,然后自己慢慢地去熟悉这些软件的操作。我是从51单片机开始入手,毕竟51单片机是最基本的了。正好这学期学习了波形的产生,就做一个基本的函数信号发生器吧。
       这次实现的函数发生器是基于51单片机的占空比、频率可调的方波发生器,适用于AT89C51/52、STV89C51/52等,现在把具体过程给大家说说。
       第1步:安装一个单片机仿真图软件,推荐使用Protues,这是一款专门用来仿真单片机的,功能十分强大。我用的是Protues8.6,下载安装步骤大家就自行查询了,学习起来比较简单,半小时就能掌握了。下面是我的仿真图形。


       第2步:单片机仿真图形界限完成之后,就是写程序了,程序写好之后,需要下载Keil软件将程序转换为单片机能识别的机器语言,即hex文件,程序可以使是c语言,也可以是汇编语言。在Keil上运行程序没有错误后就会自动生成hex文件,Keil的使用大家自行查询。下面那个“单片机1111.hex”就是我们写好的程序转换为hex文件了,其他的是附加产物。


      第3步:回到Protues仿真,双击单片机,就会弹出一个窗口,在Program File栏加入我们刚才产生的hex文件就可以了,然后点击运行就可以运行了。很多人仿真的时候可能没有示波器窗口,点击运行后,然后点击上方“调试”,再点击弹出窗口的最后一行,示波器窗口就弹出来了,就可以看到单片机产生的波形了。

下面是仿真波形图

(两张图的频率不一样,即频率可调)


(这两张图的占空比不一样,即占空比可调)

现在说说具体功能,一共有4个按键,初始频率为500Hz,第1个按键按下后频率增加500Hz,第2个按键按下后频率增加100Hz,当频率增加到最大值1MHz后重新定义为500Hz,即频率500~1MHz可变;初始占空比为50%,第3个按键按下后占空比增加10%,第4个按键按下后占空比增加1%,当占空增加到100%后就定义为0%,即占空比0%~100%可调。大家还可以稍微修改程序,实现频率和占空比在不同范围内可调。
源代码:
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
float fosc=12000000;
sbit KEY1=P1^0;
sbit KEY2=P1^1;
sbit KEY3=P1^2;
sbit KEY4=P1^3;
sbit OUTPUT=P2^0;
uchar zkb;
uint freq;
uchar TIMER0_L,TIMER0_H,TIMER1_L,TIMER1_H;
void delay(uchar n)
{
uchar i;
while(n--)
{for(i=255;i>0;i--)
{}
}}
void chushihua(void)
{
P1=0x0f;
freq=500;
zkb=50;
TIMER0_L=0xe0;
TIMER0_H=0xb1;
TIMER1_L=0xf0;
TIMER1_H=0xd8;
TL0=0xe0;
TH0=0xb1;
TL1=0xf0;
TH1=0xd8;
TMOD=0x11;
IT0=1;
EX0=1;
ET0=1;
ET1=1;
EA=1;
TR0=1;
TR1=1;
}
void main(void)
{
chushihua();
while(1)
{ }
}
void Timer0_freq() interrupt 1
{
TR1=1;
TL0=TIMER0_L;
TH0=TIMER0_H;
OUTPUT=1;
}
void Timer1_zkb() interrupt 3
{
TR1=0;
TL1=TIMER1_L;
TH1=TIMER1_H;
OUTPUT=0;
}
void keyscan() interrupt 0 using 1
{
float TF0,TZ1;
EX0=0;
delay(10);
if(P1!=0x0f)
{
if(KEY1==0) freq+=500;
if(KEY2==0) freq+=50;
if(freq>10000) freq=500;
if(KEY3==0) zkb+=10;
if(KEY4==0) zkb++;
if(zkb>100) zkb=1;
TF0=(65536-fosc/(12.0*freq));
TZ1=(65536-(fosc*zkb)/(12.0*100*freq));
TIMER0_H=(uint)TF0/256;
TIMER0_L=(uint)TF0%256;
TIMER1_H=(uint)TZ1/256;
TIMER1_L=(uint)TZ1%256;
}
P1=0x0f;
EX0=1;
TR0=1;
TR1=1;
}

评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏13 分享分享 分享淘帖 顶 踩
回复

使用道具 举报

沙发
1716867909 发表于 2019-11-19 09:15 | 只看该作者
您好,楼主,在您的keyscan()中定义了TF0,这个是stc的中断溢出标志位,您的程序跑起来没有问题么?
回复

使用道具 举报

板凳
香蕉柿布拿拿 发表于 2019-12-20 20:36 | 只看该作者
为什么我的仿真改不了占空比呢
回复

使用道具 举报

地板
Lily-lemon 发表于 2020-1-3 11:20 | 只看该作者
请问一般Proutues要从哪里下载呀?
回复

使用道具 举报

5#
2580144849 发表于 2020-1-11 11:39 | 只看该作者
诶可以哦,不错哦,真是个好思路,可以参考参考
回复

使用道具 举报

6#
丶1u0 发表于 2020-3-13 18:23 | 只看该作者
RV1是什么呀 在Proteus中怎么找呢
回复

使用道具 举报

7#
丶1u0 发表于 2020-3-13 18:23 | 只看该作者
RV1是什么东西阿 在Proteus中怎么查找呢
回复

使用道具 举报

8#
丶1u0 发表于 2020-3-13 18:53 | 只看该作者
RV1是什么东西阿
回复

使用道具 举报

9#
suqianfu 发表于 2020-4-11 21:33 | 只看该作者
丶1u0 发表于 2020-3-13 18:23
RV1是什么呀 在Proteus中怎么找呢

看图RV1应该似乎可调电阻。随便找个什么高阻值电阻就可以了。反正是测试波形用的
回复

使用道具 举报

10#
suqianfu 发表于 2020-4-11 22:17 | 只看该作者
本帖最后由 suqianfu 于 2020-4-11 22:29 编辑

大佬,我添加了一点注释,不知道理解得对不对
#include<reg51.h>                                                 //调用头文件
#define uchar unsigned char                                //
#define uint unsigned int
float fosc=12000000;                                        //声明单片机运行频率表12Mhz
sbit KEY1=P1^0;                                                        //声明按键1占用脚
sbit KEY2=P1^1;                                                        //声明按键2占用脚
sbit KEY3=P1^2;                                                        //声明按键3占用脚
sbit KEY4=P1^3;                                                        //声明按键4占用脚
sbit OUTPUT=P2^0;                                                //声明输出占用脚
uchar zkb;                                                                //声明占空比变量
uint freq;                                                                //声明频率变量
uchar TIMER0_L,TIMER0_H,TIMER1_L,TIMER1_H;//声明时间变量
void delay(uchar n)                                                //延时函数
{
uchar i;                                                            //局部整型变量
while(n--)                                                            //循环(执行次数)
{for(i=255;i>0;i--)                                                //赋值I为255,I大于0,I--自减1.直至循环执行次数清零(无中生友系列)
{}
}}
void chushihua(void)                //名为(初始化)之子函数
{
P1=0x0f;                                //赋值P1口数据
freq=500;                                //赋值频率=500
zkb=50;                                         //赋值占空比为50%
TIMER0_L=0xe0;                         //定时变量,定时器0初始时间高8位赋值,具体定时时间自己查资料
TIMER0_H=0xb1;                        //定时变量,定时器0初始时间低8位赋值,具体定时时间自己查资料
TIMER1_L=0xf0;                        //定时变量,定时器1初始时间高8位赋值,具体定时时间自己查资料
TIMER1_H=0xd8;                        //定时变量,定时器1初始时间低8位赋值,具体定时时间自己查资料
TL0=0xe0;                                //定时器0初始时间高8位赋值,具体定时时间自己查资料
TH0=0xb1;                                //定时器0初始时间低8位赋值,具体定时时间自己查资料
TL1=0xf0;                                //定时器0初始时间高8位赋值,具体定时时间自己查资料
TH1=0xd8;                                //定时器0初始时间高8位赋值,具体定时时间自己查资料
TMOD=0x11;                                //设定定时器工作模式
IT0=1;                                        //设定外部中断模式
EX0=1;                                        //启动外部中断
ET0=1;                                        //启动定时器0中断
ET1=1;                                        //启动定时器1中断
EA=1;                                        //启动全局中断使能
TR0=1;                                    //启动定时器0
TR1=1;                                    //启动定时器1
}
void main(void)                    //主程序
{
chushihua();                        //调用初始化子程序
while(1)                                //循环
{ }
}
void Timer0_freq() interrupt 1        //中断子程序--频率
{
TR1=1;                                                    //启动定时器1
TL0=TIMER0_L;                                        //定时器0高8位重新赋值定时函数,具体定时时间自己查资料
TH0=TIMER0_H;                                        //定时器0低8位重新赋值定时函数,具体定时时间自己查资料
OUTPUT=1;                                                //输出高电平
}
void Timer1_zkb() interrupt 3        //中断子程序--占空比
{
TR1=0;                                                    //关闭定时器1
TL1=TIMER1_L;                                    //定时器1高8位重新赋值定时函数,具体定时时间自己查资料
TH1=TIMER1_H;                                    //定时器1低8位重新赋值定时函数,具体定时时间自己查资料
OUTPUT=0;                                                //输出低电平
}
void keyscan() interrupt 0 using 1        //中断子程序--按键检测
{
float TF0,TZ1;                                        //声明浮点变量....这句话不是看得太懂...理解错了勿怪
EX0=0;                                                    //关闭外部中断
delay(10);                                                //延时10
if(P1!=0x0f)                                    //演示后如果检测到P1口不等于0X0F这个值,判断按键按下
{                                                                                                          
if(KEY1==0) freq+=500;                    //如果是按键1按下,则频率变量增加500
if(KEY2==0) freq+=50;                        //如果是按键2按下,则频率变量增加50
if(freq>10000) freq=500;                //如果频率变量超过10000,则频率变量重新置为500
if(KEY3==0) zkb+=10;                        //如果是按键3按下,则占空比变量增加10
if(KEY4==0) zkb++;                                //如果是按键4按下,则占空比变量增加1
if(zkb>100) zkb=1;                                //如果占空比超过100,则占空比变量重新置为1
TF0=(65536-fosc/(12.0*freq));        //定时器溢出标记TF0赋值为65536减去时钟频率12分频        ??不是太理解
TZ1=(65536-(fosc*zkb)/(12.0*100*freq));        //TZ1这个啥玩意?跟上一行大概意思吧
TIMER0_H=(uint)TF0/256;
TIMER0_L=(uint)TF0%256;
TIMER1_H=(uint)TZ1/256;
TIMER1_L=(uint)TZ1%256;
}
P1=0x0f;                                                //重新赋值P1口初始化
EX0=1;                                                    //启动外部中断
TR0=1;                                                        //重新启动定时器0TR0
TR1=1;                                                    //重新启动定时器1TR1
}
回复

使用道具 举报

11#
zhw0708 发表于 2020-7-6 00:27 | 只看该作者
这个怎么调占空比
回复

使用道具 举报

12#
我去年七岁 发表于 2021-1-12 22:03 | 只看该作者
丶1u0 发表于 2020-3-13 18:23
RV1是什么呀 在Proteus中怎么找呢

RU1是滑动变阻器。元件名是:POT-HG
回复

使用道具 举报

13#
tieq1952 发表于 2021-1-13 07:52 | 只看该作者
zkb=50;
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表