⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pwm.c

📁 本程序使用P89V51单片机利用起PCA功能对脉冲宽度可调
💻 C
字号:
/*------------------------------------------------------------------------------------*-
文 件 名:PWM.c

功    能:左右两路PWM输出,各自输出周期可调,可显示,脉冲宽度可调,可显示。
		  调试结果完全符合要求

适用器件:51系列,这里使用P89V51RB2,用STC89C54仿真

作    者:冯耿超

完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
#include "main.h"
#include "port.h"

static tWord RightPWM;
static tWord LeftPWM;
static tWord LPwmCnt;
static tWord RPwmCnt;
static tByte PwmWidth;
static tByte PwmWidt_R;
static tByte PwmWidt_L;
static tByte PwmDisp;

//显示缓冲区
tByte xdata disnum[8]={0};
//共阳数码管笔段码       0    1    2    3    4    5    6    7    8    9	    L    R    P	   -
tByte code dispcode[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0xc7,0x88,0x8c,0xbf};

static bit KeyScanTask;
static bit ShapTask;
static bit ShapCheck;
static bit KeyProcessTask;
static bit DispTask;


/*------------------------------------------------------------------------------------*-
函数原形:tByte read165(void)
函数功能:从74LS165中读数据 
输入参数:无
输出参数:无
注    意:移位问题,详细请看程序注释,先关中断,读完数据再开
作    者:冯耿超
完成日期:2007-07-10
-*------------------------------------------------------------------------------------*/
tByte Read_165()
{
	tByte dat_buf,cnt;

	//数据锁存
	_165_sft=0;

	dat_buf=0;

	cnt=8;

	//关中断
	EA = 0;

	//开始移位
	_165_sft=1;
	do
	{
		_165_clk=0;

		//这里要先移位,否则会出现最低位被补0而最高位被移出的现象
		dat_buf<<=1;

		if(_165_sda)
		{
			dat_buf|=0x01;
		}
		_165_clk=1;

	}while(--cnt);

	//开中断
   	EA = 1;

	//返回键值
	return dat_buf;	
}

void Key_Scan()
{
	if(Read_165()!=0xff)
	//检测满足,使能去抖动
	ShapCheck = 1;
}

/*------------------------------------------------------------------------------------*-
*函数原形:Key_Board_Process()
*函数功能:键盘功能处理函数
*输入参数:无
*输出参数:无
*修改情况:修改按键功能,使得对应显示时所以对应的按键才起作用,语句请看程序中带*号的语句
*修改日期:200707-20
*作    者:冯耿超
*完成日期:2007-07-12
-*------------------------------------------------------------------------------------*/
void Key_Board_Process()
{
	if(Read_165()!=0xff)
	{
		switch(Read_165())
		{
			//左边PWM加
			case 0xfe: if(PwmDisp ==0) //*
					   {
					  		 LeftPWM++;
				      		 if(LeftPWM>999)
			          		 LeftPWM = 999;
					   }
				       break;
			//左边PWM减
			case 0xfd: if(PwmDisp ==0) //*
						{
							if(LeftPWM!=0)
							LeftPWM--;
							else
			           		LeftPWM = 1;
						}
				       	break;

			//右边PWM加
			case 0xfb: if(PwmDisp ==1)	//*
					   {
					   		RightPWM++;
				       		if(RightPWM>999)
			           		RightPWM = 999;
					   }
				       break;
			//右边PWM减
			case 0xf7: if(PwmDisp ==1) //*
						{
					   		if(RightPWM!=0)
					   		RightPWM--;
					   		else 
			           		RightPWM = 1;
					    }
				       break;

			//脉冲宽度加
			case 0xdf: if(PwmDisp ==2)	//*
						{
							 PwmWidth++;
				      		 if(PwmWidth>250)
			          		 PwmWidth = 250;
					   	}
					   break;
			//脉冲宽度减
			case 0xbf: if(PwmDisp ==2)
						{
							if(PwmWidth!=0)
					   		PwmWidth--;
					    	else
					   		PwmWidth = 0;
						}
					   break;

			//选择显示值
			case 0x7f: PwmDisp++;
						if(PwmDisp==5)PwmDisp = 0;
						break;

			default:  break;
		}
	}
	
}

/*------------------------------------------------------------------------------------*-
函数原形:void _138_drive_bit(tByte xdata led_bit)
函数功能:led位驱动,用74LS138作为位选,位选参数为led_bit
输入参数:无
输出参数:无
作    者:冯耿超
完成日期:2007-06-13
-*------------------------------------------------------------------------------------*/
void _138_drive_bit(tByte xdata led_bit)
{
	switch(led_bit)
		{
			case 1:_138_a = 0;_138_b = 0;_138_c = 0; break;

			case 2:_138_a = 1;_138_b = 0;_138_c = 0; break;

			case 3:_138_a = 0;_138_b = 1;_138_c = 0; break;

			case 4:_138_a = 1;_138_b = 1;_138_c = 0; break;

			case 5:_138_a = 0;_138_b = 0;_138_c = 1; break;

			case 6:_138_a = 1;_138_b = 0;_138_c = 1; break;

			case 7:_138_a = 0;_138_b = 1;_138_c = 1; break;

			case 8:_138_a = 1;_138_b = 1;_138_c = 1; break;

			default:break;
		}
}

/*------------------------------------------------------------------------------------*-
函数原形:void _164_send_data(tByte xdata led_code)
函数功能:将共阳数码管笔段通过74LS164送出
输入参数:无
输出参数:无
注    意:74LS164为串入并出,时钟先为上升沿触发,先关中断,送完数据再开
作    者:冯耿超
完成日期:2007-06-13
-*------------------------------------------------------------------------------------*/
void _164_send_data(tByte xdata led_code)
{
	tByte data i;


	 EA = 0;
	for(i=0;i<8;i++)
		{
		_164_sck = 0;

		_164_sda = (led_code&0x80);

		_164_sck = 1;

		led_code<<=1;
		}
	  EA = 1;
	_164_sck = 0;

}
/*------------------------------------------------------------------------------------*-
函数原形:void display_led(tByte xdata led,tByte xdata num)
函数功能:把显示笔段num送到指定的LED上,指定位为led
输入参数:无
输出参数:无
注    意:LED动态显示按照关显示——>送笔段——>开显示的步骤,防止LED串红
作    者:冯耿超
完成日期:2007-06-13
-*------------------------------------------------------------------------------------*/
void display_led(tByte xdata led,tByte xdata num)
{
	//关LED显示
	_138_oe = 1;

	//送显示笔段到指定的LED上
	switch(led)
		{
			case 1:_138_drive_bit(led);
					_164_send_data(num);break;

			case 2:_138_drive_bit(led);
					_164_send_data(num);break;

			case 3:_138_drive_bit(led);
					_164_send_data(num);break;

			case 4:_138_drive_bit(led);
					_164_send_data(num);break;

			case 5:_138_drive_bit(led);
					_164_send_data(num);break;

			case 6:_138_drive_bit(led);
					_164_send_data(num);break;

			case 7:_138_drive_bit(led);
					_164_send_data(num);break;

			case 8:_138_drive_bit(led);
					_164_send_data(num);break;
			default:break;
					
		}
	//开LED显示
	_138_oe = 0;	
}

/*------------------------------------------------------------------------------------*-
函数原形:void display()
函数功能:在LED上从右到左显示按键按下所对应的值
输入参数:无
输出参数:无
注    意:参数K的取值
作    者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void display()
{
	static tByte  cnt;
	switch (PwmDisp)
	{
		//显示左边PWM值
		case 0:
		disnum[0] = LeftPWM%10;
		disnum[1] = LeftPWM/10%10;
		disnum[2] = LeftPWM/100%10;
		disnum[3] = LeftPWM/1000%10;
		//显示“L”
		disnum[4] = 13;
		disnum[5] = 13;
		disnum[6] = 13;
		disnum[7] = 10;
		break;

	   	//显示右边PWM值
		case 1:
		disnum[0] = RightPWM%10;
		disnum[1] = RightPWM/10%10;
		disnum[2] = RightPWM/100%10;
		disnum[3] = RightPWM/1000%10;
		//显示“R”
		disnum[4] = 13;
		disnum[5] = 13;
		disnum[6] = 13;
		disnum[7] = 11;	
		break;

		//显示脉冲高电平的宽度
		case 2:
		disnum[0] = PwmWidth%10;
		disnum[1] = PwmWidth/10%10;
		disnum[2] = PwmWidth/100%10;
		disnum[3] = PwmWidth/1000%10;
		//显示“P”
		disnum[4] = 13;
		disnum[5] = 13;
		disnum[6] = 13;
		disnum[7] = 12;
		break;

		//右边脉冲计数
		case 3:
		disnum[0] = RPwmCnt%10;
		disnum[1] = RPwmCnt/10%10;
		disnum[2] = RPwmCnt/100%10;
		disnum[3] = RPwmCnt/1000%10;
		disnum[4] = 13;
		disnum[5] = 13;
		disnum[6] = 12;
		disnum[7] = 11;	
		break;

		//左边脉冲计数
		case 4:
		disnum[0] = LPwmCnt%10;
		disnum[1] = LPwmCnt/10%10;
		disnum[2] = LPwmCnt/100%10;
		disnum[3] = LPwmCnt/1000%10;
		disnum[4] = 13;
		disnum[5] = 13;
		disnum[6] = 12;
		disnum[7] = 10;	

		break;
		default:break;
	}

	//LED显示器显示
	if(++cnt==8)cnt=0;
	switch(cnt)
	{
		case 0:display_led(cnt+1,dispcode[disnum[7]]);break;
		case 1:display_led(cnt+1,dispcode[disnum[6]]);break;	
		case 2:display_led(cnt+1,dispcode[disnum[5]]);break;	
		case 3:display_led(cnt+1,dispcode[disnum[4]]);break;	
		case 4:display_led(cnt+1,dispcode[disnum[3]]);break;	
		case 5:display_led(cnt+1,dispcode[disnum[2]]);break;	
		case 6:display_led(cnt+1,dispcode[disnum[1]]);break;	
		case 7:display_led(cnt+1,dispcode[disnum[0]]);break;
		default:break;		
	}	
}

/*------------------------------------------------------------------------------------*-
函数原形:void T0_Init()
函数功能:T0初始化,工作方式1,16位定时计数器,定时5ms作为系统时标
输入参数:无
输出参数:无
作    者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void T0_Init()
{
	TMOD &=0xf0;
	TMOD |=0x01;

	//5ms中断初值
	TH0 = 0xec;
	TL0 = 0x78;

	ET0   = 1;
	TR0   = 1;
	EA    = 1;
}

/*------------------------------------------------------------------------------------*-
函数原形:void Task_Init()
函数功能:系统任务初始化
输入参数:无
输出参数:无
作    者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void Task_Init()
{
	//左右脉冲周期初始化
	RightPWM = 100;
	LeftPWM  = 100;
	LPwmCnt  = 0;
	RPwmCnt  = 0;

	//用于调节高电平脉冲的宽度,
	//它必须小于脉冲周期,即PWM的中断周期
	PwmWidth = 30;

	//左右边高电平脉冲宽度
	PwmWidt_R = 0;
	PwmWidt_L = 0;

	//默认显示脉冲宽度值
	PwmDisp = 2;

	//键盘扫描任务初始化
	KeyScanTask = 0;

	//去抖动初始化
 	ShapCheck = 0;

	//键盘处理任务初始化
 	KeyProcessTask = 0;

	//LED显示任务初始化
	DispTask = 0;

}

/*------------------------------------------------------------------------------------*-
函数原形:系统主函数
函数功能:由PwmWidth可以计算高电平的宽度为5ms*20=100ms
          低电平的宽度为RightPWM或是LeftPWM的初值乘以5ms减去100ms
输入参数:无
输出参数:无
作    者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void main()
{
    //系统初始化
	T0_Init();
	Task_Init();

	while(1)
	{
		//执行LED显示任务
		if(DispTask)
		{	
			DispTask = 0;
			display();	
		}

		//保持高电平时间为PwmWidt_R*5ms
		if(PwmWidt_R>0)
		{
			Right = 1;
		
		}
		else
		{
			Right = 0;
			
		}

		//保持高电平时间为PwmWidt_L*5ms
		if(PwmWidt_L>0)
		{
			Left  = 1;
		}
		else
		{
			Left  = 0;
		}

		//键盘扫描任务
		if(KeyScanTask)
		{
			KeyScanTask = 0;
			Key_Scan();
		}

		//键盘处理任务
		if(KeyProcessTask)
		{
			KeyProcessTask = 0;
			Key_Board_Process();
		}
	}
}

/*------------------------------------------------------------------------------------*-
函数原形:void T0_ISR()
函数功能:T0中断服务子程序
输入参数:无
输出参数:无
作    者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void T0_ISR() interrupt 1 using 1
{	
	
	//右边PWM宽度
	static tByte RightPwmCnt = 0;
	//左边PWM宽度
	static tByte LeftPwmCnt  = 0;

	//键盘扫描
	static tByte KeyCnt      = 0;
	//键盘去抖动
	static tByte ShapCnt     = 0;


	//重装初值
	TH0 = 0xec;
	TL0 = 0x78;
	
	//添加显示任务
	DispTask = 1;

	//添加右边PWM输出
	if(++RightPwmCnt==RightPWM)
	{
		RightPwmCnt = 0;
		PwmWidt_R = PwmWidth;
		Right = 1;
		RPwmCnt++;	
	}

	//添加左边PWM输出
	if(++LeftPwmCnt==LeftPWM)
	{
		LeftPwmCnt  = 0;
		PwmWidt_L = PwmWidth;
		Left  = 1;
		LPwmCnt++;	
	}

	//150ms也就是30个时标执行一次键盘扫描
	if(++KeyCnt==30)
	{	
		KeyCnt = 0;
		KeyScanTask = 1;
	}

	//是否允许去抖动开始
	if(ShapCheck)
	{	
		//去抖动时间为10ms
		if(++ShapCnt==2)
		{
			//去抖动关闭
			ShapCheck = 0;

			//去抖动时长复位
			ShapCnt = 0;

			//添加键盘处理任务
			KeyProcessTask = 1;
		}
	}
	
	//左右两边PWM宽度调节
	//注意这里的“--”符号位置
	if(PwmWidt_R--==0)PwmWidt_R = 0;
	if(PwmWidt_L--==0)PwmWidt_L = 0;
	
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -