一个TIMER软件模拟输出6个PWM

【嵌入式底层知识修炼】软件模拟周期占空比可调的PWM(+源代码) 文章目录01 - 软件模拟思路02 - 核心源码03 - 软件模拟结果04 - 源码链接05 - 总结 任何事情都不应满足只会使用,而是要求懂得原理。 ————小白   需要软件模拟PWM的理由,一般是硬件PWM的IO端口不够用,因为软件模拟PWM不稳定,准确性也比较差,所以在硬件PWM可用的情况下是不会使用软件模拟的,但无论怎样,软件模拟的作用也很大,可以作为测试用例、通用PWM、替补方... 阅读详情

51单片机的timer很少,往往16bit的timer只有一个,可以硬件输出的channel也不多。如我手上这就是这个情况,timer1, 16bit, 只有三个通道。我想输出6个pwm,这个就无法硬件的方式了。只能使用软件模拟了。

方法1.

pwm是1-20ms一个周期,要是设置一个固定的周期,中断间隔就必须比较小,如0.01ms , 这个精度已经不怎么样了,但是每秒还是需要100000次中断,对于51来说,这个压力很大,实测效果很差。

方法2 我们使用分时办法来进行。

      1. pwm需要每20ms对pin脚操作两次,周期开始设置它为1,到达指定时间时设置它为0,这个宽度,在航模的信号里,是1-2ms之间。我们将6个通道,每个分时3ms给它做这第一项的两次设置。 

       2. 每20ms一周期,在一个周期里有12个状态. 分别是 CH_N_ON和CH_N_OFF.   n 取值1-6

       

   分时如上图,这样的话,我们只需要中断600次一秒就可以输出6个通道的PWM了

代码:


void et2()interrupt 12{                                //TIMER 2
		

#if DEBUG

#else	
	u16 tmp;
        TR2_CLR
       //
       //0: 0-   3-                           20
       //1    C1-3 
       //2:      3-   6-                      20  -23
       //3         C2 -6
       //4:           6-  9 -                 20     -26
       //5             C3-9
       //6:               9-   12 -           20         -29
       //7                 C4-12
       //8:                    12 -  15       20              -32
       //9                        C5-15
       //10:                          15- 18  20                  -35
       //11                            C6-18  
//T2H = T2L=0;

        switch(g_pwm_out_state)
        {
                case 0: 
											
                        CH1 = 1; 
                        tmp = g_pwm[0];
                        break;
                case 1:
									
                        CH1 = 0;
                        tmp = 3000-g_pwm[0];
                        break;
                case 2: 
                        CH2 = 1;
                        tmp = g_pwm[1];
                        break;
                case 3:
                        CH2 = 0;
                        tmp = 3000-g_pwm[1];
                        break;
                case 4:
                        CH3 = 1;
                        tmp = g_pwm[2];
                        break;
                case 5:
                        CH3 = 0;
                        tmp = 3000-g_pwm[2];
                        break;
                case 6: 
                        CH4 = 1;
                        tmp = g_pwm[3];
                        break;
                case 7:
                        CH4= 0;
                        tmp = 3000-g_pwm[3];
                        break;
                case 8:
                        CH5 = 1;
                        tmp = g_pwm[4];
                        break;
                case 9:
                        CH5= 0;
                        tmp = 7928-g_pwm[4];
                        break;
        }
        
        tmp = 0xffff - tmp*2;
        T2H =  tmp>>8;
        T2L =  tmp&0xff;
        
       
      
				if(g_pwm_out_state==11)
						g_pwm_out_state = 0;
				else 
					  g_pwm_out_state++;
				
        TR2_SET;
#endif				
				

}

   方法3. 上面的中断次数还是过多,可以进一步减少中断,可以先排序,然后执行,中断最多是pwm数量的N+1, 以下是伪代码


#include <stdio.h>
#include <stdint.h>

uint16_t    pwm_ticks[7];
uint8_t     pwm_index[7];
uint16_t 	nextTicks[8];
uint8_t     is_need_update=0; 
uint8_t     state;

uint16_t pwm_to_ticks(uint16_t src)
{
	return  src;
}

void update_pwm(uint16_t * src)
{
	uint8_t ischanged = 0;
	uint16_t ticks;
	uint8_t idx1 ;
	uint8_t idx2;
	for(int i = 0;i < 7; i++)
	{
		ticks = pwm_to_ticks(src[i]);
		if(pwm_ticks[i]!=ticks)
		{
			pwm_ticks[i] = ticks;
			ischanged = 1;
		}

	}

	if(ischanged)
	{
		for(int i=0;i<7;i++)
		{
			pwm_index[i] = i;
		}

		for(int i=0;i<6;i++)
		{
			for(int j=0;j<6-i;j++)
			{
				idx1  = pwm_index[j];
				idx2  = pwm_index[j+1];
				if(pwm_ticks[idx1] > pwm_ticks[idx2])
				{
					pwm_index[j]  = idx2;
					pwm_index[j+1] = idx1;
				}
			}
		}

		idx1  = pwm_index[0];
		nextTicks[0]  = pwm_ticks[ idx1 ];
		for(int i=1;i<7;i++)
		{
			idx1  = pwm_index[i];
			idx2  = pwm_index[i-1];
			ticks = pwm_ticks[ idx1 ] -  pwm_ticks[ idx2 ];
			nextTicks[i]= ticks; 
		}
		
		nextTicks[7]= pwm_to_ticks(20000) -pwm_ticks[pwm_index[6]] ;
		state = 0;
	}


}


void timer_int()
{
  
   for(int i=0;i<8;i++)
   {
	   if(state == 7)
	   {
	   		printf("-----set all pins----------\n");
	   		state  = 0;
	   }
	   else
	   {
	   		printf("reset pin %d\n", pwm_index[state]);
	   		 state ++ ;

	   }

	   uint16_t tt = nextTicks[state];
	 

	   if(tt > 10)
	   {
	   	    printf("set tick %u\n", tt);
	   	 	break;
	   }
	   else 
	   {	
	   	 	printf("too small %u , continue \n", tt);
	   		continue;
	   }
	}


  
}


 int main(int argc, char const *argv[])
{
	uint16_t pwms[][7] =
	{

	 {1500,1000,1600,1799,2300,1500,1500},
	 {1500,1000,1600,1400,2300,1500,1000},
	 {1000,1000,1600,1300,2300,1500,2000},
	 {1000,1250,1600,1799,2300,1500,1000},

	};

	for(int k =0;k< 4;k++)
	{


		printf("########### test round #########\n");
		update_pwm(pwms[k]);
		state = 7;
		for(int i= 0;i < 20 ;i ++)
		{	
			printf("********interrupt %u***********\n", i);
			timer_int();
		}
	}
	return 0;
}

输出比较(OC) PWM 的三大重要参数计算以及端口重映射,末尾附上软件仿真 IC(input Capture):输入捕获CC(Capture /Compare):输入捕获,输出比较单元输出比较可以通过比较CNT和CCR寄存器值的关系,对输出电平进行置1,置0的反转操作,用于输出一定频率的占空比PWM波形高级定时器和通用定时器有四个输出比较通道什么是PWM:全称是脉冲宽度调制PWM参数:频率1/Ts 占空比=Ton/Ts 分辨率=占空比变化步距TON=高电平比上Ts的时间TOFF:表示低电平比上Ts的时间TS:表示周期。 阅读详情

相关推荐

stm32定时器输出pwm&IO口模拟pwm——呼吸灯

stm32定时器输出pwm实现呼吸灯的效果

wer4567的博客 9830

软件模拟PWM写法

软件模拟PWM推法

admwtf的博客 3532

---GD32F3X0---TIMER14使用TIMER1的代码移植,无法输出PWM

问题描述: 使用TIMER1可以正常输出PWM,但是将TIMER1替换成TIMER14却无法输出PWM,两者有什么不一样的吗?TIMER1代码如下: /*! \brief configure the GPIO ports \param[in] none \param[out] none \retval none */ void gpio_co...

qq_23852045的博客 2122

STM32软件模拟输出PWM(HAL库)

PSC,CCR的值,计数器寄存器从0开始递增或者递减,到了设定的CCR值进行电平的反转,这样就做到了在一个定时器周期内存在连续的高低电平,进而输出一个0-3.3V之间的模拟电压,既然知道了原理,只要把每一部分用软件替换就可以。我们知道在单片机上许多硬件能实现的操作都能通过软件模拟,比如USARR,I2C等通信协议。这里分享一种软件模拟输出PWM的方法。(2)用一定时器开启定时中断,这里的中断频率设置和采用定时器通道输出频率一致即可。该方法适用于定时器数量不够时,可以让一个定时器同时执行中断和输出PWM。

WYZ12345_的博客 2294

GD32F103 配置timer0输出PWM

编译环境: 库:GigaDevice.GD32F10x_DFP.2.0.2 编译器:KEIL 5.30 芯片:GD32F103C8 这句话很重重要,不配置输不出PWM的。 timer_primary_output_config(TIMER0,ENABLE); C文件 #include "gd32f10x_libopt.h" /******************************************************************** Function...

qq_34152804的博客 5971

【RoboMaster】PWM?计时器Timer?我们马上解决掉他们!【STM32】

同步博客地址:从STM32开始的RoboMaster生活:进阶篇 VI [Timer & PWM] 项目&教程仓库:-STM32-RoboMaster- 1.0 什么是Timer?PWM又是什么玩意儿? 1.1 Timer定义 Timer就是定时器,但是与一般我们生活中使用的定时器不同的是,Timer的单位不是s秒,而是Hz赫兹,而且通常是MHz(1,000,000Hz),定时...

qq_35739721的博客 2095

AURIX TC397 Timer PWM 之 GPT12 CCU6

目录GPT12_Timer_InterruptGPT12_Timer_ConcatenationGPT12_PWM_GenerationGPT12_PWM_CaptureCCU6_InterruptCCU6_PWM_GenerationCCU6_PWM_Capture微信公众号 GPT12_Timer_Interrupt GPT12模块的定时器T3产生500ms中断翻转LED: GPT12(The General Purpose Timer Unit)包含两个GPT块(GPT1和GPT2), 每个块都有几

weifengdq的专栏 8106

GD32C103RBT6 TIMER2_CH0(PA6)固定占空比 PWM 输出实战

本文分享 GD32C103C 单片机基于 TIMER2 通道 0(PA6 引脚)输出固定占空比 PWM 波的完整代码实现,采用查询式 SysTick 延时(无中断依赖),代码结构清晰、可直接移植,适合新手快速上手 GD32 PWM 开发。

weixin_52594377的博客 628

使用HAL库开发STM32:使用Timer输出PWM信号

文章目录目的基础说明输出PWM信号总结 目的 单片机输出PWM信号是很常用的一种功能需求,STM32中通常使用Timer来输出PWM信号,这篇文章将对相关内容做个说明。 基础说明 在使用Timer输出PWM信号需要了解一些Timer的基础知识,可以查看 《使用HAL库开发STM32:Timer基础说明与定时功能使用》 这篇文章中的 Timer基础说明 章节。 输出PWM信号 总结 ...

Naisu的各种笔记 2万+

【10】GD32H7xx Timer PWM

GD32H7xx 定时器PWM输出调试记录

madao1234的博客 1057

ADS 2024 Circuit Envelope 仿真:3步配置调制源,分析5G NR EVM与ACPR

本文详细介绍了ADS 2024 Circuit Envelope仿真技术在5G NR调制源配置与关键指标分析中的应用。通过3步配置调制源,工程师可以高效评估EVM与ACPR等关键指标,特别适用于功放设计中的非线性效应诊断。文章还提供了优化仿真设置和精确提取EVM、ACPR的实用方法,助力5G射频前端设计。

weixin_28559803的博客 383

GD32 Timer+ADC多通道+DMA+PWM调试记录

本例记录使用GD32307C开发板,实现以内部Timer1 CH1为触发源,触发ADC0的两个通道,进行并行非连续采样,病通过DMA传输采样结果。同时输出PWM,用来检测Timer1 CH1的触发周期。

man9953212的博客 5701

手把手调试Android 14 ShellTransitions:用Log追踪ActiveTransition与Handler匹配过程

本文详细介绍了在Android 14中调试ShellTransitions机制的方法,重点通过Log追踪ActiveTransition与Handler的匹配过程。文章提供了关键日志点植入策略、Handler匹配机制解析及实战调试案例,帮助开发者解决窗口转场动画中的卡顿和错乱问题,提升系统动画性能。

weixin_42652674的博客 429

【GD32】GD32设置TIMER0输出PWM

GD32的ADC有很多模式,这里教大家使用最简单的软件触发读取ADC,我使用的芯片是 GD32F350K8U6 使用步骤 1.初始化IO引脚设置为模拟输入模式 2.初始ADC时钟和配置ADC 3.软件触发读取ADC值 4.将ADC值转换成电压值 我这里采用PA5进行ADC的channel_5通道 1.初始IO引脚 /*初始化时钟*/ rcu_periph_clock_enable(RCU_GPIOA); /* 配置读取ADC引脚 */ gpio_mode_set(ADC_GPIO_PORT, GPIO_MO

xxdx_admin的博客 1万+

EndNote20保姆级安装教程:从下载到汉化一步到位(附闪退解决方案)

本文提供EndNote20的详细安装与汉化教程,涵盖系统准备、分步安装、路径优化及闪退解决方案。针对科研人员的需求,特别优化了中文环境下的使用体验,确保文献管理工具的高效稳定运行。

weixin_30415113的博客 339

保姆级教程:用VESTA软件5分钟搞定纳米颗粒Wulff Construction建模(以Au金为例)

本文提供了一份详细的VESTA软件教程,指导用户如何在5分钟内完成纳米颗粒Wulff Construction建模,特别以金(Au)为例。从环境搭建、基础晶体结构创建到Wulff Construction设置和可视化优化,涵盖了全流程操作步骤和常见问题解决方案,适合科研新手快速上手。

weixin_29311017的博客 436

DMA+Timer 产生 PWM 多出一个波形问题

我在调试ws2812的时候吗,发现灯光的数据一直和预先的不符合,简化了程序逻辑依然看不出有什么问题,最后通过逻辑分析仪发现,PWM多出了一个波形,明显的,这导致所有的数据错了一位。 事实上这不是硬件上的BUG,猜测原因:因为每一次DMA请求是Timer的溢出中断产生的,所以在DMA请求前,就已经有一个PWM产生了,那么从时序看上,就多出一个PWM波形,但这并不是DMA产生的。 问题回顾 uint16_t test_arr[48] = { 59,29,59,5

白杨木屋 2990

MIPI CSI-2数据加扰技术解析:LFSR实现与多通道同步策略

本文深入解析了MIPI CSI-2协议中的数据加扰技术,该技术通过线性反馈移位寄存器(LFSR)对图像数据进行随机化处理,旨在有效降低高速传输中的电磁干扰。文章详细阐述了LFSR的工作原理、D-PHY与C-PHY物理层不同的初始化与同步策略,并探讨了多通道同步的实现方法,为高速接口的EMC设计与调试提供了实践指导。

weixin_29207149的博客 385

MSPM0开发学习笔记:TIMER-PWM

本文介绍了MSPM0开发中的PWM(脉宽调制)功能,重点讲解了PWM的原理、配置及程序设计。PWM通过定时器生成可调占空比的方波,广泛应用于电机调速、LED亮度控制等场景。文章详细说明了如何在syscfg中配置PWM的频率、相位和输出引脚,并提供了简单的代码示例。此外,还介绍了如何通过按钮控制PWM的占空比,展示了进阶应用的可能性。通过本文,读者可以掌握PWM的基本配置和编程方法,并了解其在嵌入式系统中的实际应用。

2403_87969572的博客 2005

SCL3400硬件避坑指南:引脚兼容性测试+模式切换异常处理

本文针对村田SCL3400双轴倾角计在实际硬件设计中的常见问题,提供了详细的避坑指南。重点解析了从SCL3300迁移时的引脚兼容性真相,包括SPI时序、电源去耦等关键差异,并深入探讨了模式切换中超量程错误的复现原因与软件容错方案。同时,指导了如何根据应用场景配置低通滤波器,并给出了无自动唤醒功能下的可靠电源与长线SPI通信管理策略。

weixin_29190573的博客 292

YOLO26涨点改进 |TGRS 2026 | 全网独家改进、特征融合创新篇 | 引入CMIFE跨模态交互特征提取模块,充分挖掘互补信息,适合多模态图像融合,目标检测,实例分割,遥感图像分类高效涨点

本文提出CMIFE模块用于改进YOLO26网络模型,通过跨模态交互特征提取实现多尺度特征的精细化融合。该模块采用双注意力机制(自注意力强化模态内一致性,交叉注意力实现跨模态互补信息筛选),有效解决了传统YOLO融合中的信息冗余和负迁移问题。实验表明,CMIFE模块在保持模型实时性的同时,显著提升了小目标检测、遮挡场景等复杂环境下的检测精度。文章详细介绍了模块原理、网络结构、核心代码实现方法,并提供了三种改进版YOLO26的配置文件方案(yolo26_CMIFE-1/2/3.yaml)。

Ai缝合怪、助力小伙伴高效发论文 147

STM32F103的timer3四路PWM

正点原子精英板PWM输出略改一下 STM32103定时器3输出四路PWM 通过串口控制四个舵机 timer.c 若是没用到中断之类的 只输出PWM 定时器这样初始话就够了 #include "timer.h" void TIM3_PWM_Init(u16 arr,u16 psc)//arr:自动重装值 //psc:时钟预分频数 { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStruc

aw的博客 1752

Android 12.0 系统systemui状态栏下拉左滑显示通知栏右滑显示控制中心模块的流程分析

在android12.0的系统rom定制化开发中,在系统原生systemui进行自定义下拉状态栏布局的定制的时候,需要在systemui下拉状态栏下滑的时候,根据下滑坐标来 判断当前是滑出通知栏还是滑出控制中心模块,所以就需要根据屏幕宽度,来区分x坐标值为多少是左滑出通知栏或者右滑出控制中心模块,接下来分析下 状态栏下滑坐标等相关功能代码分析

安卓兼职framework和app工程师的博客 2768

利用高级定时器产生6路互补PWM波形输出

STM32的MCU产品自带定时器资源十分丰富,既有专门用于电机控制的高级定时器(TIMER1/TIMER8),又有仅定时用的基本定时器(TIMER6/TIMER7) 和通用定时器(TIMER2/3/4/5/14/15/16/17) ,还有2个看门狗定时器和一个Systick定时器,真是十分强大哈。 这一篇文章就来讲解一下利用高级定时器TIMER1产生6路互补PWM波形输出。第一部分讲...

duobaohongtu3的专栏 5189

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

STM32F0 Timer1 作为PWM设置

设置PA7 作为 PWM输出 void TIM_IO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;// GPIO_InitStructure.GPIO_...

cym_anhui的专栏 3640

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: 高通Qualcomm处理器的手机或设备进EDL 9008模式的办法
下一篇: MacOs 下用于性能优化的“perf“命令
aerror
博客等级 码龄23年 392粉丝 193原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值