赞
踩
如果芯片内部模块能集成输出,一般观察手册或者芯片IO口都会标明这个是否是PWM口
如果没有集成PWM功能,可以通过IO口软件模拟,
如图为SG90舵机模块,黄色为PWM信号控制,红色和褐色分别为VCC和GND。当输入的PWM的占空比不同的时候,舵机模块的摆头幅度不同。一般情况如下:
接下来通过C51单片机输出PWM波控制舵机的摆头(黄线连接P1.1口)代码如下:
#include <REGX52.H> sbit sg_90 = P1^1;//黄线连接P1.1口 int cnt = 0; //标志位 int jd; //占空比的分子 void Delay300ms() //@11.0592MHz { unsigned char i, j, k; i = 3; j = 26; k = 223; do { do { while (--k); } while (--j); } while (--i); } void Delay2000ms() //@11.0592MHz { unsigned char i, j, k; i = 15; j = 2; k = 235; do { do { while (--k); } while (--j); } while (--i); } void Timer0_Init(void) { TMOD = 0x01;//配置定时器T0为16位定时器 TL0 = 0x33; //定时器计1个数为1.085us,则当舵机为0度的时候,需要0.5ms, TH0 = 0xFE; //则定一个0.5ms的定时器 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; EA = 1; } void main(void) { Delay300ms();//给硬件准备时间 Timer0_Init(); jd = 1;//一上电,开始占空比为1/40,为0度 cnt = 0; sg_90 = 1;//先给输出引脚一个高电平 while(1) {//角度由135度到0度来回摆动 jd = 4; cnt = 0; Delay2000ms(); jd = 1; cnt = 0; Delay2000ms(); } } void Timer0_Handler() interrupt 1//中断函数 { cnt++; TL0 = 0x33; TH0 = 0xFE; //控制PWM波的占空比 if(cnt < jd){ sg_90 = 1; }else{ sg_90 = 0; } if(cnt == 40){ cnt = 0; sg_90 = 1; } }
/*通过超声波模块控制LED1灯的亮灭,当手靠近超声波模块时,灯亮*/ #include <REGX52.H> sbit Trig = P1^5; sbit Echo = P1^6; sbit LED1 = P3^7; void Delay10us() //@11.0592MHz { unsigned char i; i = 2; while (--i); } void Timer0_Init(void) { TMOD = 0x01; TL0 = 0; TH0 = 0;//设置定时器T0从0开始数数 } void Delay200ms() //@11.0592MHz { unsigned char i, j, k; i = 2; j = 103; k = 147; do { do { while (--k); } while (--j); } while (--i); } void main(void) { double time; double dis; Timer0_Init(); while(1){ Delay200ms();//先给单片机准备时间 //1、开始发波 Trig = 0; Trig = 1; Delay10us(); Trig = 0; //2、检测ECHO引脚电平 while(Echo == 0); TR0 = 1; //启动定时器 while(Echo == 1); TR0 = 0; //关闭定时器 //3、计算定时开到定时关的时间(计算数的个数), //2位二进制11,01。合并位1101怎么算?1101为(11*2^2)+01=13 time = (TH0 * 256 + TL0) * 1.085; //us为单位 //34000cm/s = 34cm/ms = 0.034cm/us dis = time * 0.017; //cm为单位 if(dis < 10) { LED1 = 0; }else { LED1 = 1; } TL0 = 0; TH0 = 0;//定时器清零,以便下次测距 } }
代码优化②:
/**优化:将定时器0改为定时器1,然后将超声波测距封装成为一个函数*/ #include <REGX52.H> sbit Trig = P1^5; sbit Echo = P1^6; sbit LED1 = P3^7; void Delay10us() //@11.0592MHz { unsigned char i; i = 2; while (--i); } void Delay200ms() //@11.0592MHz { unsigned char i, j, k; i = 2; j = 103; k = 147; do { do { while (--k); } while (--j); } while (--i); } void Timer1_Init(void) { //使用定时器1 TMOD &= 0x0F; TMOD |= 0x10; TL1 = 0; TH1 = 0;//设置定时器T1从0开始数数 } double get_distance()//超声波获得距离的函数 { double time; TL1 = 0; TH1 = 0;//定时器清零,以便下次测距 //1、开始发波 Trig = 0; Trig = 1; Delay10us(); Trig = 0; //2、检测ECHO引脚电平 while(Echo == 0); TR1 = 1; //启动定时器 while(Echo == 1); TR1 = 0; //关闭定时器 //3、计算定时开到定时关的时间(计算数的个数), //2位二进制11,01。合并位1101怎么算?1101为(11*2^2)+01=13 time = (TH1 * 256 + TL1) * 1.085; //us为单位 //34000cm/s = 34cm/ms = 0.034cm/us return (time * 0.017); //cm为单位; } void main(void) { double dis; Timer1_Init(); while(1){ Delay200ms();//先给单片机准备时间 dis = get_distance(); if(dis < 10) { LED1 = 0; }else { LED1 = 1; } } }
舵机和超声波代码整合,舵机用定时器0,超声波用定时器1。
1、实现物体靠近后,自动开盖,2秒后关盖。
2、查询的方式添加按键控制
3、 查询的方式添加震动控制
#include <REGX52.H> sbit SW1 = P2^1;//按键SW1连接的是P2.1口 sbit Trig = P1^5; sbit Echo = P1^6; sbit LED1 = P3^7; sbit sg_90 = P1^1;//黄线连接P1.1口 sbit vibrate = P3^2;//震动传感器连接P3.2口,使用外部中断0 int cnt = 0; //标志位 int jd; //占空比的分子 int vib_mark; //震动传感器的标志位 void Delay10us() //@11.0592MHz { unsigned char i; i = 2; while (--i); } void Delay200ms() //@11.0592MHz { unsigned char i, j, k; i = 2; j = 103; k = 147; do { do { while (--k); } while (--j); } while (--i); } void Delay2000ms() //@11.0592MHz { unsigned char i, j, k; i = 15; j = 2; k = 235; do { do { while (--k); } while (--j); } while (--i); } void EX0_Init()//触发中断0初始化 { EX0 = 1; EA = 1; IT0 = 0;//低电平触发 } void Timer0_Init(void)//定时器T0中断初始化 { TMOD &= 0xF0;//配置定时器T0为16位定时器 TMOD |= 0x01; TL0 = 0x33; //定时器计1个数为1.085us,则当舵机为0度的时候,需要0.5ms, TH0 = 0xFE; //则定一个0.5ms的定时器 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; EA = 1; } void Timer1_Init(void)//定时器T1初始化 { //使用定时器1 TMOD &= 0x0F; TMOD |= 0x10; TL1 = 0; TH1 = 0;//设置定时器T1从0开始数数 } double get_distance() { double time; TL1 = 0; TH1 = 0;//定时器清零,以便下次测距 //1、开始发波 Trig = 0; Trig = 1; Delay10us(); Trig = 0; //2、检测ECHO引脚电平 while(Echo == 0); TR1 = 1; //启动定时器 while(Echo == 1); TR1 = 0; //关闭定时器 //3、计算定时开到定时关的时间(计算数的个数), //2位二进制11,01。合并位1101怎么算?1101为(11*2^2)+01=13 time = (TH1 * 256 + TL1) * 1.085; //us为单位 //34000cm/s = 34cm/ms = 0.034cm/us return (time * 0.017); //cm为单位; } void sg90_0(void) //舵机0度 { sg_90 = 1;//先给输出引脚一个高电平 jd = 1;//一上电,开始占空比为1/40,为0度 cnt = 0; } void sg90_90(void)//舵机90度 { sg_90 = 1;//先给输出引脚一个高电平 jd = 3;//一上电,开始占空比为3/40,为90度 cnt = 0; } void main(void) { double dis; Timer0_Init(); Timer1_Init(); EX0_Init(); sg90_0(); while(1){ Delay200ms();//先给单片机准备时间 dis = get_distance(); if(dis < 10 || SW1 == 0 || vib_mark == 1) { vib_mark = 0; LED1 = 0; sg90_90(); Delay2000ms(); } else { LED1 = 1; sg90_0(); Delay200ms(); } } } void Timer0_Handler() interrupt 1//中断函数 { cnt++; TL0 = 0x33; TH0 = 0xFE; //控制PWM波的占空比 if(cnt < jd){ sg_90 = 1; }else{ sg_90 = 0; } if(cnt == 40){ cnt = 0; sg_90 = 1; } } void EX0_Handler() interrupt 0//触发中断0函数 { vib_mark = 1; }
为什么我们使用震动传感器控制的时候不直接if(dis < 10 || SW1 == 0 || vibrate== 0)喃?这样判断不是跟简单吗?
原因:因为震动传感器因为震动而发出的低电平0不仅微弱,而且时间比较断。当震动传感器给出低电平的时候,而单片机还在执
Delay2000ms();而当进入判断的时候,可能震动传感器发出的低电平已经消失了,已经变成高电平了。这样就会导致震动传感器不灵敏。
所以,通过外部中断来改变标志位,这样就会规避这个问题。当震动时,触发中断,标志位变为1,等待判断。只有进入判断后标志位才变回0。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。