怎么用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调节占空比的主要内容,如果未能解决你的问题,请参考以下文章