51单片机流水灯制作
文章目录
- 51单片机流水灯制作
- 初试与复试
- 初试
- 复试
- 制作
- 分工
- 制作之硬件
- 制作之软件
- 总结
51单片机流水灯制作
我不得不承认自己的毅力实在是太差了,原先准备一星期写一篇博客的目标一致没有实现,挺惭愧的。前几日忙着参加学院里面关于飞思卡尔竞赛的选拔赛,被要求制作一个用51单片机实现的流水灯和呼吸灯,耗费了我不少时间。现将其记录于此。
初试与复试
初试
原本我是不打算参加初试的,让我朋友参加,积累经验,我专心鼓捣制作机器人,但我的朋友说我应该试试,耗费时间,但有利于我实现制作机器人的计划。
初试还算简单, 主要考察了数电模电以及c语音和飞思卡尔小车的实现思路等。我两手空空就去了考场,遭到了朋友的笑话,说我要是能入选就奇了怪了。因为开卷,所以基本上数电模电翻书找即可,实现思路之类的只好跟朋友共享其事先打印的资料了。好在脸皮厚,成功抵挡了无数白眼。c语言部分也不算难,有个位移的还算有趣,就是对<<符号的考察。
初试结果自然出乎我朋友的意料,我通过了,而两位朋友成功落选。我自然很是得意。不过他们向组织方表达了自己对飞思卡尔的向往后,还是被允许参加复试了。
复试
任务:
- 设计并制作基于 51 单片机的流水灯,通过按键实现 LED 灯点亮模
式的切换。 - 用 DXP 画出附录原理图, 并保存为.SchDoc 格式。
要求
- 基本要求:( 80 分)
- 所搭建的板子至少 8 个 LED 灯, 4 个按键,可实现至少四种 LED
点亮模式的变换。( 50 分) - 上交一份设计报告, 报告内容包括组元分工、硬件及软件设计,另需在附录附上完整程序。( 20 分)
- 上交附录原理图。( 10 分)
- 发挥部分:( 20 分)
- 电路布局美观, 无虚焊, 焊点光度锥度好。
- 程序运行稳定。
- 其他。
制作
分工
我为队长,其他两人分别攻硬件DXP与软件编程。
先期我们的主要任务是了解51单片机的大致流程,求广,防止方向的选择错误。
制作之硬件
后来因为大家事都挺忙的,我就扛起了大梁。
在网上买了所需的东西后,我便照着葫芦画瓢,在面包板上搭建电路。第一次是在晚上11点左右,果不其然的搭建错了。没有考虑面包板的结构。后来就改正了。
在网上买的PL2303的USB转ttl线出了问题,刚开始是引脚缺杜邦头,后来我自己想办法给焊上去个杜邦头,还是不能用,貌似其内部短路,向周围的人借,也没有借到。只好从网上买了个ch340的usb转ttl线,谢天谢地,这个很好用。usb转ttl线就这样耽误了一个星期。在此处记录一下,usb转ttl线需要驱动,通过该线向单片机里烧录程序需要另外的软件,我用的是STC-ISP(竟然有个每次下载前都重新装载目标文件选项,坑死我了,不知有多少回我忘记点这个选项而没有装载修改后的hex文件去烧录,看着不能按照预期工作的单片机,又投入到keil中,好悲惨的回忆)。
吐槽一下硬件,好多坑啊。电烙铁用到还行,因为没有买用来飞线的导线,用心疼焊锡,只好把排针上金属针拔下来一个一个的焊当导线,好悲催啊。中间好几回虚焊漏焊,其中艰难不足为外人道也。
制作之软件
因为负责软件工作的同学比较忙,所以我接手了程序的开发。他只完成了基本的流水灯部分,而且冗余的代码超多,我又优化了一番。比如说对开关引脚的判断,竟然用了4个if语句,还没有考虑全,我简单几句就搞定了,还有对开关变化的判断。然后开始添加新功能。
因为我觉得单个的呼吸灯可能会比较没有竞争力,所以准备不光实现灯的强度在时间维度上呈三角波,还要实现灯的强度在空间维度上呈三角波,甚至要实现灯的强度在时间、空间上同时呈三角波(就是初相位相差一定值的平行三角波),想想就美好。不光要有三角波,还要有正弦波,再多加个强度相差一定值的正弦波(感觉这个比初相位相差一定知道正弦波计算量小些)。
需求想好了,就该实现了。我需要手动完成PWM。总结了一下实现PWM的几种思路:
- 用一个定时器定时,如以100μs为一个小周期,然后以100个小周期为一个PWM周期(10ms),频率为100Hz(1s / 10 ms)。在定时器中断处理函数里直接输出buffer中的数据,或者在处理函数中处理判断后输出数据。
- 用一个定时器定时,比如给通电时间(占空比 * PWM周期)定时,然后在中断处理函数中启动不通电时间(PWM周期 - 通电时间)的定时,可用原来的定时器也可用其他的定时器。
- 用一个定时器给PWM周期定时,然后用另外一个定时器给通电时间(也可以是不通电时间)定时。
综合考虑后我选择第一种方法,噩梦才刚刚开始。
如何实现不同相位的多个平行正弦波的呼吸灯?使用小周期只是提供了实现的可能而已,还不够。后来在操场上散步时,才想明白了实现方式。我靠自己想明白了单极性脉宽调制的原理,相当激动啊。换句话说,我是使用单极性脉宽调制来实现想法的。设置一个数组来存储八个LED的占空比,占空比在时间维度上不断的沿正弦函数变化,在每个小周期的定时器中断时通过占空比与小周期计数的大小比较来确定引脚输出。
然而实际效果不尽人意,几乎就是随意的亮灭。后来我通过keil的仿真是才发现,51单片机的计算能力太差,中断处理函数结束时,下一次中断几乎就出来了。对八个占空比的处理不能在完整的一次完成。几天的努力还是没能实现的自己当初的想法,有些失落,虽然自己收获了不少,特别是感觉自己对c语言小技巧的使用(有些自大啦啦啦)。
后来也就没办法,只好不用正弦波,只用三角波了。三角波也能玩出新花样。比如两排强度互补的呼吸灯。其实就是在传统意义上的不通电时间时输出对在通电时期输出数据的取反,比如在通电时间是输出0x00,在不通电时间输出0xFF。
总结
其他的好像也没什么可以说的了。好不容易能沉下心写会技术博客的感觉真好!!!
附录:
程序之现实版
#include <STC89C5xRC.H>
#include<intrins.h>
#include<math.h>
#include<stdlib.h>/*************全局常量***********/
#define uchar unsigned char
#define uint unsigned int
#define LED P1/*****************全局变量**********************************/
uchar changed, status_switch = 0x00, status_switch_pre = 0x00; //status_switch记录当前开关状态,status_switch_pre记录此前开关状态,changed记录开关状态是否变化
uchar counter_PWM = 0, length_PWM = 100; //定时器0的两次中断时间为一小周期,length_PWM个小周期组成一个PWM大周期,counter_PWM负责计数,表示进行到了PWM大周期中的第counter_PWM个小周期
uchar duty_cycle, buffer; //duty_cycle是占空比,buffer缓冲LED点亮信息uchar i, j;
float f;/*******************部分流水灯模式所需信息**************/
uchar code table1[]={0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfc,0xff};//0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f 左移
uchar code table2[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf};
uchar code table3[]={0x7e,0xbd,0xdb,0xe7,0xff};
uchar code table4[]={0xaa,0x55};/*********函数声明********************/
void delay1ms();
void delay(uint x);
void keyscan();
void close_int();
void init_pwm();
void timer0_init(void);/********主函数****************************/
void main()
{while(1) {keyscan(); switch (status_switch) { //根据status_switch选择灯的点亮模式case 0x00:LED = 0xFF;break;case 0x01:for (i=0;i<16;i++) { LED=table1[i]; delay(200);keyscan(); if (changed)break; } break; case 0x02:for(i=0;i<14;i++) { LED=table2[i]; delay(300);keyscan(); if (changed)break; }break; case 0x03:for(i=0;i<5;i++) {LED=table3[i]; delay(250);keyscan(); if (changed)break; }break; case 0x04:for(i=0;i<2;i++){LED=table4[i]; delay(3000);keyscan(); if (changed)break; }break; case 0x05:for (i=0;i<16;i++) { LED=~table1[i]; delay(200);keyscan(); if (changed)break; } break; case 0x06:for(i=0;i<14;i++) { LED=~table2[i]; delay(500);keyscan(); if (changed)break; }break; case 0x07:for(i=0;i<5;i++) {LED=~table3[i]; delay(250);keyscan(); if (changed)break; }break; case 0x08:case 0x09:case 0x0A:case 0x0B:timer0_init();while (1) {init_pwm(); for (i = 100; i > 0; i-=5) {duty_cycle = i; //改变占空比 delay(25);keyscan(); //开关检测if (changed) {close_int(); //关闭定时器break; //退出for语句}}break; //退出switch语句for (i = 0; i < 100; i+=5) {duty_cycle = i;delay(25);keyscan(); //在该case语句中设置两个keyscan是为了更快的对开关变化做出响应if (changed) {close_int();break;}}break; }break;case 0x0C: case 0x0D:timer0_init();while (1) {init_pwm(); delay(250); //因为不需要改变有时间维度上的变化,所以没有占空比的改变,只需延时即可keyscan();if (changed) {close_int();break;}}break;case 0x0E:srand(counter_PWM); //设置随机数种子while(1) {LED = rand() % 256; //保证随机数在0与255之间delay(200);keyscan();if (changed)break; }break; case 0x0F: LED = 0x00;break;} }
}/*****************延时1ms*************/
void delay1ms() //@11.0592MHz
{unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i);
} /**********延时x毫秒***********/
void delay(uint x)
{ uchar k;for(k=x; k>0; k--)delay1ms();
}/****开关检测。修改开关状态(status_switch)以及更改标志(changed)****/
void keyscan()
{ status_switch_pre = status_switch; //记录此前的开关状态P2 = 0xFF; //准备读取P2status_switch = (~P2) & 0x0F; //读取P2并提取有用信息changed = (status_switch != status_switch_pre); //判断是否改变
} /*****关闭定时器0***********************/
void close_int()
{TR0 = 0;//关闭Timer0ET0 = 0;//关闭T0中断LED = 0xFF;//PWM输出高电平
}/*************PWM的初始化******/
void init_pwm()
{buffer = 0x00; //清空缓存区duty_cycle = 0; //归零占空比
}/*****************定时器0************************/
void timer0_init(void) //100微秒@11.0592MHz
{AUXR &= 0x7F; //定时器时钟12T模式TMOD &= 0xF0; //设置定时器模式TMOD |= 0x02; //设置定时器模式TL0 = 0xA4; //设置定时初值TH0 = 0xA4; //设置定时重载值TF0 = 0; //清除TF0标志TR0 = 1; //定时器0开始计时ET0=1; //开启T0中断EA=1; //开总中断
}/*************定时中断1处理程序****************/
void timer0_int(void) interrupt 1
{switch (status_switch) {case 0x08:
/**********单个灯光强度在时间维度上呈三角形波*********/buffer = 0x00;buffer |= (counter_PWM < duty_cycle) << 0;break; case 0x09:
/*****两半灯光强度在空间维度上呈互补三角形波**************************/if (counter_PWM < duty_cycle)buffer = 0xF0;elsebuffer = 0x0F;break;case 0x0A:
/*****交叉灯的灯光强度在空间维度上呈互补三角形波**************************/if (counter_PWM < duty_cycle)buffer = 0x55;elsebuffer = 0xAA;break;case 0x0B:
/*****全部灯光强度在时间维度上呈三角形波***************/if (counter_PWM < duty_cycle)buffer = 0xFF;elsebuffer = 0x00; break;case 0x0C:
/***************灯光强度在空间维度上呈中凸三角形波************/if (counter_PWM < 5) //原谅我不是完美的三角形波buffer = 0xFF;else if (counter_PWM < 20)buffer = 0x7E;else if (counter_PWM <45)buffer = 0x3C;elsebuffer = 0x18;break;case 0x0D:
/********灯光强度在空间维度上呈中凹三角形波******************/if (counter_PWM < 5)buffer = _crol_(0xFF, 4);else if (counter_PWM < 20)buffer = _crol_(0x7E, 4);else if (counter_PWM <45)buffer = _crol_(0x3C, 4);elsebuffer = _crol_(0x18, 4);break; }LED = ~buffer; //引脚的1代表灯灭,所以反转一下if (counter_PWM >= length_PWM) counter_PWM = 0; //将counter_PWM限制在0到length_PWM之间counter_PWM++;
}
程序之理想版
#include <STC89C5xRC.H>
#include<intrins.h>
#include<math.h>/*************全局常量***********/
#define uchar unsigned char
#define uint unsigned int
#define LED P1
#define PI 3.1415926uchar i, j;
float f;/*****************全局变量**********************************/
uchar changed, status_switch = 0x00, status_switch_pre = 0x00;
uchar counter_PWM = 0, length_PWM = 100, buffer;
uchar duty_cycles[8];/*******************部分流水灯模式所需信息**************/
uchar code table1[]={0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfc,0xff};//0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f 左移
uchar code table2[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf};
uchar code table3[]={0x7e,0xbd,0xdb,0xe7,0xff};
uchar code table4[]={0xaa,0x55};/*********函数声明********************/
void delay1ms();
void delay(uint x);
void keyscan();
void close_int();
void init_pwm();
void timer0_init(void);/********主函数*************/
void main()
{while(1) {keyscan(); switch (status_switch) {case 0x00:LED = 0xFF;break;case 0x01:for (i=0;i<16;i++) { LED=table1[i]; delay(200);keyscan(); if (changed)break; } break; case 0x02:for(i=0;i<14;i++) { LED=table2[i]; delay(300);keyscan(); if (changed)break; }break; case 0x03:for(i=0;i<5;i++) {LED=table3[i]; delay(250);keyscan(); if (changed)break; }break; case 0x04:for(i=0;i<2;i++){LED=table4[i]; delay(300);keyscan(); if (changed)break; }break; case 0x05:for (i=0;i<16;i++) { LED=~table1[i]; delay(200);keyscan(); if (changed)break; } break; case 0x06:for(i=0;i<14;i++) { LED=~table2[i]; delay(300);keyscan(); if (changed)break; }break;case 0x07:for(i=0;i<5;i++) {LED=~table3[i]; delay(250);keyscan(); if (changed)break; }break; case 0x08:
/**********灯光强度在时间维度上呈三角形波*********/ timer0_init();while (1) {init_pwm();for (i = 100; i > 0; i--) {duty_cycles[j] = i;buffer |= ((counter_PWM < duty_cycles[j]) << j); //j代表第几盏灯,第一盏灯时j=0,因为没有指明j,所以每次都有不同的灯作为呼吸灯亮 delay(50);keyscan();if (changed) { //在此插入changed的判断语句是为了更好的对开关变化做出响应close_int();break;}}for (i = 0; i < 100; i++) {duty_cycles[j] = i;buffer |= ((counter_PWM < duty_cycles[j]) << j); //j代表第几盏灯,第一盏灯时j=0; delay(50);keyscan();if (changed) {close_int();break;}}}break; case 0x09:
/***************灯光强度在空间维度上呈三角形波************/timer0_init();for (j = 0; j < 4; j++)duty_cycles[j] = 25 * j;for (j = 4; j < 8; j++)duty_cycles[j] = 25 * (8 - j);while (1) {init_pwm(); for (j = 0; j < 8; j++) {buffer |= ((counter_PWM < duty_cycles[j]) << j); }
// if (counter_PWM < 25)
// buffer = 0xFF;
// else if (counter_PWM < 50)
// buffer = 0x7E;
// else if (counter_PWM < 75)
// buffer = 0x3C;
// else
// buffer = 0x18;delay(100);keyscan();if (changed) {close_int();break;} }break; case 0x0A:
/********灯光强度在时间、空间维度上呈三角形波******************/timer0_init();while (1) {init_pwm();for (i = 0; i < 100; i++) { for (j = 0; j < 4; j++)duty_cycles[j] = (25 * j + i) % 100;for (j = 4; j < 8; j++)duty_cycles[j] = (25 * (8 - j) + i) % 100; for (j = 0; j < 8; j++)buffer |= ((counter_PWM < duty_cycles[j]) << j); delay(50);keyscan();if (changed) {close_int();break;}}for (i = 100; i > 0; i--) { for (j = 0; j < 4; j++)duty_cycles[j] = (25 * j + i) % 100;for (j = 4; j < 8; j++)duty_cycles[j] = (25 * (8 - j) + i) % 100; for (j = 0; j < 8; j++)buffer |= ((counter_PWM < duty_cycles[j]) << j); delay(50);keyscan();if (changed) {close_int();break;}}}break; case 0x0B:
/*****灯光强度在时间维度上呈正弦***************/timer0_init();while (1) {init_pwm(); for (i = 0; i < 180; i++) {duty_cycles[j] = (uchar)(sin(PI * i / 180) * 100); //j号灯为呼吸灯buffer |= ((counter_PWM < duty_cycles[j]) << j); delay(50);keyscan();if (changed) {close_int();break;}}}break;case 0x0C:
/*****灯光强度在空间维度上呈正弦波**************************/timer0_init();while (1) { init_pwm();for (j = 0; j < 8; j++) {duty_cycles[j] = (uchar)(sin(PI * ((int)(i + 22.5 * j) % 180) /180) * 100);buffer |= (counter_PWM < duty_cycles[j]) << j;}delay(100); keyscan();if (changed) {close_int();break;}}break; case 0x0D:
/**灯光强度在时间维度上呈三角形波,空间上为相位相差22.5度的数个平行正弦波**/timer0_init();while (1) {init_pwm(); for (i = 0; i < 180; i++) {buffer = 0x00;for (j = 0; j < 8; j++) {duty_cycles[j] = (uchar)(sin(PI * ((int)(i + 22.5 * j) % 180) /180) * 100);buffer |= (counter_PWM < duty_cycles[j]) << j;}delay(10);keyscan();if (changed) {close_int();break;}}}break;case 0x0E:
/*灯光强度在时间维度上呈正弦波,空间上为强度相差22级的平行正弦波*/timer0_init();while (1) { for (i = 0; i < 180; i++) {buffer = 0x00;for (j = 0; j < 8; j++) {
// duty_cycles[j] = (uchar)((int)(sin(PI * i / 180) * 100 + 22) % 100);duty_cycles[j] = (uchar)(sin(PI * i / 180) * 100 + 22);buffer |= (counter_PWM < duty_cycles[j]) << j;}delay(10);keyscan();if (changed) {close_int();break;}}}break;case 0x0F: LED = 0x00;break;} }
}/*****************延时1ms*************/
void delay1ms() //@11.0592MHz
{unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i);
} /**********延时x毫秒***********/
void delay(uint x)
{ uchar k;for(k=x; k>0; k--)delay1ms();
}/****开关检测。修改开关状态(status_switch)以及更改标志(changed)****/
void keyscan()
{ status_switch_pre = status_switch;P2 = 0xFF;status_switch = (~P2) & 0x0F; changed = (status_switch != status_switch_pre);
} /*****关闭定时器0***********************/
void close_int()
{TR0 = 0;//关闭Timer0ET0 = 0;//关闭T0中断LED = 0xFF;//PWM输出高电平
}/*************PWM的初始化******/
void init_pwm()
{uchar k;buffer = 0x00;for (k = 0; k < 8; k++)duty_cycles[k] = 0;
}/*****************定时器0************************/
void timer0_init(void) //100微秒@11.0592MHz
{AUXR &= 0x7F; //定时器时钟12T模式TMOD &= 0xF0; //设置定时器模式TMOD |= 0x02; //设置定时器模式TL0 = 0xA4; //设置定时初值TH0 = 0xA4; //设置定时重载值TF0 = 0; //清除TF0标志TR0 = 1; //定时器0开始计时ET0=1; //开启T0中断EA=1; //开总中断
}/*************定时中断1处理程序****************/
void timer0_int(void) interrupt 1
{ LED = ~buffer; //引脚的1代表灯灭,所以反转一下if (counter_PWM >= length_PWM)counter_PWM = 0; //将counter_PWM限制在0到length_PWM之间counter_PWM++;
}
51单片机流水灯制作相关推荐
- 51单片机流水灯现象1
**` 51单片机流水灯程序 `** 初学者小白,分享上课时写的小程序和普中科技程序,感兴趣的可以瞧瞧. 程序如有相同冒犯了. /*********************************** ...
- 如何用多种代码实现51单片机流水灯
用51单片机实现流水灯的方法有多种:普通电灯法,位移法,数组法.其次模拟电路的电流图也与点一盏LED相似.具体如下: 一 首先是最简单的代码: #include <reg52.h> ...
- 51单片机-------流水灯(实验报告)
实验二:LED流水灯 一.实验目的 掌握51单片机开发板的使用步骤: 掌握51单片机开发板所需软件的安装过程: 以LED流水灯实验为例子,掌握软件KEIL4的使用方法. 二.实验设备 实验仪器设备: ...
- 51单片机——流水灯
51单片机--单向流水灯 编写程序8个LED灯从最高位依次点亮,每次只亮一盏灯,依此循环. 电路原理图 源代码 #include "reg51.h" #define u8 unsi ...
- 设计一:51单片机流水灯控制
目录 一.设计内容 二.硬件电路分析 三.仿真原理图 四.程序设计 五.仿真结果 六.思考题 作者有话说 一.设计内容 本次设计使用4个按键,当KEY1按下时,P0口所接的发光二极管(D1~D8)以1 ...
- 51单片机流水灯的三种实现方法
首先,介绍下原理.下图为主控芯片和流水灯模块的原理图.流水灯模块接在单片机的P1口,由原理图可以知道,在P1口给一个低电平即可点亮LED灯.相反,如果要LED灯熄灭,就要把P1口的电平变为高电平即可. ...
- 51单片机流水灯控制(5种方法)
实现流程: 1. 新建一个工程(新项目)2. 查看原理图确定需求(流水灯)对应LED引脚在单片机上的哪个引脚3. 编写程序(通过查看原理图可以看到我们需要控制整个P2口以控制8个LED灯)4. 编译程 ...
- 51单片机流水灯用c语言,51单片机之流水灯(C语言和汇编两个版本)
c语言 #include typedef unsigned char uint8; sbit ADDR0=P1^0; sbit ADDR1=P1^1; sbit ADDR2=P1^2; sbit EN ...
- 51单片机流水灯:控制LED亮暗顺序
我这个是笨方法,就是根据需要LED位置,将二进制转化为16进制去控制其亮. 比如让LED灯按照从右往左数1,3,5,7,8,6,4,2的顺序依次亮,就根据二进制转16进制的表 表示出来即可,代码如下: ...
最新文章
- 如何配置sharepoint moss集成RMS
- static_cast, dynamic_cast, const_cast
- 未能添加对***.dll的引用 问题解决方法
- 路由跟踪工具0trace
- 均分纸牌pascal程序
- 《openssl 编程》之 DH
- SAP Spartacus Translation (翻译) 相关话题
- 前端:收集前端开发者手边必备的11款神器
- Node.js与io.js那些事儿
- 48.孩子们的游戏(圆圈中最后剩下的数)
- python基础高级函数(十九)
- es6 去掉空格_es6 filter() 数组过滤方法总结
- QuickHit游戏
- ACM 算法详细分类
- ISO 28000供应链安全管理简述及标准
- 如何选择erp进销存订单管理软件?
- SHA-512摘要算法(带示例)
- Unix 环境高级编程(一):开发环境
- 例题 9-2 巴比伦塔(The Tower of Babylon, UVa 437)
- JavaCV开发详解之1:调用本机摄像头并预览摄像头图像画面视频(建议使用javaCV最新版本)