怎么用51单片机来实现pwm调节占空比

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎么用51单片机来实现pwm调节占空比相关的知识,希望对你有一定的参考价值。

可以用两级中断来实现,该方法可实现精确占空比与频率的调整。。。
大致思想是:
1.在定时器T0中开定时器T1,并使pwm=1,
2.在定时器T1中关掉T1,即TR1=0;并使pwm=0;

注:t0控制频率(周期),t1控制占空比(高电平时间),你只要装载相应的定时器初值即可。很实用的哦
参考技术A 写一个函数咯,输入占空比,比如fun(x,y),高电平持续x毫秒,低电平持续(y-x)毫秒,while(1)fun(x,y)无限循环此函数 参考技术B 用定时来做

STC单片机通过ADC外部输入调节PWM占空比输出并串口打印当前脉冲值

【STC单片机】通过ADC外部输入调节PWM占空比输出并串口打印当前脉冲值


本示例演示基于STC15W408AS单片机,采用自制开发板,使用的是外部晶振16MHz。

这篇文章涉及了综合知识的运用。写这一篇文章的初衷是昨天睡觉前的想法,做一个外部可控可调的PWM输出的功能。今天将其程序整理出来并且验证了这一点。

注意:ADC输入引脚不能浮空,不然PWM输出将会是跳动的脉动输出。占空比将是一个随机的状态形式输出。

  • 功能实现内容:占空比可调,但是频率是固定的2.6666KHz的。如需调整频率,需要修改想要的宏定义的参数。

f=MAIN_Fosc /PWM_DUTY

/**PWM.h文件**/
#define		PWM_DUTY		6000		//定义PWM的周期,数值为PCA所选择的时钟脉冲个数。
#define		PWM_HIGH_MIN	80			//限制PWM输出的最小占空比, 避免中断里重装参数时间不够。
#define		PWM_HIGH_MAX	(PWM_DUTY - PWM_HIGH_MIN)		//限制PWM输出的最大占空比。
  • 设置 2KHz的频率
//频率f=MAIN_Fosc /PWM_DUTY
#define		PWM_DUTY		8000		//定义PWM的周期,数值为PCA所选择的时钟脉冲个数。
#define		PWM_HIGH_MIN	80			//限制PWM输出的最小占空比, 避免中断里重装参数时间不够。
#define		PWM_HIGH_MAX	(PWM_DUTY - PWM_HIGH_MIN)		//限制PWM输出的最大占空比。

  • 调节定位器时,串口监视器数据动态输出
  • 代码完善后,调试完成的输出,串口只是为了方便调试和观察,程序测试OK后,可以去掉串口功能的部分。

采用的是10K定位器接到到P1.3引脚上,作为外部模拟量输入

  • 接线图
  • ADC的基准电压源采用P1.1口
P1ASF = 0;
Get_ADC10bitResult(1);	//改变P1ASF后先读一次并丢弃结果, 让内部的采样电容的电压等于输入值.
Bandgap = Get_ADC10bitResult(1);	//读P1.1的电压为基准ADC, P1ASF=0, 0通道的电压值
  • PWM输出通道

采用的是PCA0,也可以切换到PCA1,或者PCA2,

  • 选择任意其中一组即可。
		if(pwm0 >= PWM_HIGH_MAX)	pwm0 = PWM_HIGH_MIN;
		PWMn_Update(PCA0,pwm0);

		if(pwm1 >= PWM_HIGH_MAX)	pwm1 = PWM_HIGH_MIN;
		PWMn_Update(PCA1,pwm1);

		if(pwm2 >= PWM_HIGH_MAX)	pwm2 = PWM_HIGH_MIN;
		PWMn_Update(PCA2,pwm2);
  • PWM输出引脚
PCA0----P25
PCA1----P26
PCA2----P27
  • 示波器采集的数据波形

  • 最大占空比时的波形
  • 最小占空比

ADC模拟量数据转换

该函数数据类型必须满足被处理的数据,在这个函数实现上掉坑里了,刚开始认为定义一个无符号整型类型来存放就够了,转换出来的值完全不是预期值。

  • 该函数实现了,将ADC采集的模拟量的值范围0~1023,转换为80 ~ 5920对应值。
/********************** 模拟量映射函数 ************************/
long map( long x,  long in_min,  long in_max,  long out_min,  long out_max)

  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

  • 说明一点的是:STC15W408AS只有2个定时器,全部用完了。定时器0用于ADC采集,定时器2用于串口波特率发生器。STC15W408AS是没有定时器1的。
  • ADC采集有2个方案

方式一是直接采用读值,另一种是采集16次去平均值。通过宏定义选择其一即可。

	#define	Cal_MODE 	0	 //每次测量只读1次ADC. 分辨率0.01V
	#define	Cal_MODE 	1	//每次测量连续读16次ADC 再平均计算. 分辨率0.01V

主程序代码


/*************	功能说明	**************

双串口全双工中断方式收发通讯程序。
注意:
通过PC向MCU发送数据, MCU收到后通过串口把收到的数据原样返回.

******************************************/
#include	"config.h"
#include "stdio.h"
#include "USART.h"
#include "PWM.h"
#include "ADC.h"

	#define	Cal_MODE 	0	 //每次测量只读1次ADC. 分辨率0.01V
//	#define	Cal_MODE 	1	//每次测量连续读16次ADC 再平均计算. 分辨率0.01V
//typedef 	unsigned char	u8;
//typedef 	unsigned int	u16;
//typedef 	unsigned long	u32;

volatile bit	B_4ms;			//1ms标志

volatile u16	msecond;		//计时
u16	Bandgap;	//

void  delay_ms(unsigned int ms)

     unsigned int i;
	 do
	      i = MAIN_Fosc / 13000;
		  while(--i)	;   //14T per loop
     while(--ms);

/********************** 模拟量映射函数 ************************/
long map( long x,  long in_min,  long in_max,  long out_min,  long out_max)

  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;


//========================================================================
// 函数: void main(void)
// 描述: 主函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注: 
//========================================================================
void main(void)

			u8 i;
	int	Duty ;
	int	j;
	
			PCA_Init();	//PCA初始化
//	P0M1 = 0;	P0M0 = 0;	//设置为准双向口
//	P1M1 = 0;	P1M0 = 0;	//设置为准双向口
	P1M1 = 0x00; P1M0 = 0x00; 
	P1M1 |= (1<<3);		// 把ADC口设置为高阻输入
	P1M0 &= ~(1<<3);
	P1ASF = (1<<3);		//P1.3做ADC
	ADC_CONTR = 0xE0;	//90T, ADC power on
//	P2M1 = 0;	P2M0 = 0;	//设置为准双向口
	P2M1 &= ~(0xe0);	//P2.7 P2.6 P2.5 设置为推挽输出
	P2M0 |=  (0xe0);
	P3M1 = 0;	P3M0 = 0;	//设置为准双向口
//	P4M1 = 0;	P4M0 = 0;	//设置为准双向口
//	P5M1 = 0;	P5M0 = 0;	//设置为准双向口
//	P6M1 = 0;	P6M0 = 0;	//设置为准双向口
//	P7M1 = 0;	P7M0 = 0;	//设置为准双向口
		//	S1_USE_P30P31();
	Timer0Init();//定时器0初始化
 InitSerialPort(); 		//初始化串口,波特率9600,8bit数据位,1停止位,无校验
	EA = 1;	//允许总中断

//	PrintString1("STC15F2K60S2 UART1 Test Prgramme!\\r\\n");	//SUART1发送一个字符串

	while (1)
	
	//	delay_ms(500);	
		P10 = !P10;
		if(pwm0 >= PWM_HIGH_MAX)  pwm0 = PWM_HIGH_MAX;
		
//		if(pwm1 >= PWM_HIGH_MAX)	pwm1 = PWM_HIGH_MAX;
//		PWMn_Update(PCA1,pwm1);

//		if(pwm2 >= PWM_HIGH_MAX)	pwm2 = PWM_HIGH_MAX;
//		PWMn_Update(PCA2,pwm2);
	if(B_4ms)	//5ms到达标志位
		
			B_4ms = 0;
			if(++msecond >= 250)	//计数到达1S
			
				msecond = 0;
			#if (Cal_MODE == 0)
			//=================== 只读1次ADC, 10bit ADC. 分辨率0.01V ===============================
				P1ASF = 0;
				Get_ADC10bitResult(1);	//改变P1ASF后先读一次并丢弃结果, 让内部的采样电容的电压等于输入值.
				Bandgap = Get_ADC10bitResult(1);	//读P1.1的电压为基准ADC, P1ASF=0, 0通道的电压值
	
				P1ASF = 1<<3;
				j = Get_ADC10bitResult(3);	//读外部电压ADC
			//	j = (u16)((u32)j * 507 / Bandgap);	//计算外部电压, Bandgap为5.07V, 测电压分辨率0.01V
					printf("\\t ADC =%d \\t\\n",j);	
				pwm0 = (u16)map(j, 0, 1023,80, 5920);//将映射的值赋值给pwm0
				 
				printf("\\t pwm0 =%u \\t\\n",pwm0 );
				#endif
			//==========================================================================

			//===== 连续读16次ADC 再平均计算. 分辨率0.01V =========
			#if (Cal_MODE == 1)
				P1ASF = 0;
				Get_ADC10bitResult(1);	//改变P1ASF后先读一次并丢弃结果, 让内部的采样电容的电压等于输入值.
				for(j=0, i=0; i<16; i++)
				
					j += Get_ADC10bitResult(1);	//读P11内部基准ADC, P1ASF=0, 读1通道
				
				Bandgap = j >> 4;	//16次平均
				P1ASF = (1<<3);		//P1.3做ADC
				for(j=0, i=0; i<16; i++)
				
					j += Get_ADC10bitResult(3);	//读外部电压ADC
				
				j = j >> 4;	//16次平均
				pwm0= (u16)map(j, 0, 1023, 80, 5920);//将映射的值赋值给pwm0
				//pwm0 = j;
					printf("\\t pwm0 =%d \\t\\n",j);	
				//j = (u16)((u32)j * 507 / Bandgap);	//计算外部电压, Bandgap为5.07V, 测电压分辨率0.01V
			#endif		
			//==========================================================================
	
			//Duty=map(pwm0, 80, 5920, 0, 100);//将pwm0转换为占空比串口输出
			PWMn_Update(PCA0,pwm0);//PCA0--P25,PCA2--P26
		//	printf("占空比:%u \\r\\n",Duty);
			
			

	//	printf("pwm0:%u \\t pwm1:%u \\t pwm2:%u\\r\\n",pwm0,pwm1,pwm2);	//SUART1发送一个字符串
	


/********************** Timer0 1ms中断函数 ************************/
void timer0 (void) interrupt 1

	B_4ms = 1;		//1ms标志


  • 程序架构

程序源码

链接:https://pan.baidu.com/s/1pTDkLEafN-MFihnq4l1fNQ 
提取码:xonl

以上是关于怎么用51单片机来实现pwm调节占空比的主要内容,如果未能解决你的问题,请参考以下文章

51单片机实现pwm对电机调速

单片机ad与pwm问题

51单片机PWM

AVR单片机教程——PWM调光

一块51单片机如何输出4个程序,执行4个不同功能?

STC单片机通过ADC外部输入调节PWM占空比输出并串口打印当前脉冲值