最近需要用到单片机输出SPWM波功能,在网上找了好多资料,发现都不完整,有算法的没有代码,有代码的看不懂算法。于是只好自己摸索,现将方法整理如下。
关于什么是SPWM波,为什么要用SPWM波,网上的介绍有很多,就不多说了。主要说一下在STM32F103C8T6上是如何实现的。
要产生SPWM波,核心就是调节PWM波的占空比,在一定时间段内使输出PWM波所占的面积和对应的正弦波面积相等。占空比的调节需要根据正弦波上对应的点来调节。那么首先就是要生成一组正弦波数据。生成正弦波的数据代码如下:

//point 一个周期内的点数
//maxnum 最大值
void get_sin_tab1( u16 point, u16 maxnum )
{u16 i = 0, j = 0, k = 0;float hd = 0.0;            //弧度float fz = 0.0;        //峰值u16 tem = 0;j = point / 2;            //水平线位置 单片机没有负电压hd = PI / j;           // π/2 内每一个点对应的弧度值k = maxnum / 2;          //最大值一半for( i = 0; i < point; i++ ){     fz = k * sin( hd * i ) + k;                    tem = ( u16 )fz ;                      printf("%d\r\n",tem);sinData[i] = tem;}

通过串口波形软件可以看到生成的数据和波形如下:

数据生成好之后,下来设置定时器输出PWM,

void TIM1_PWM_Init(u16 arr, u16 psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_OCInitTypeDef TIM_OCInitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //复用时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);TIM_TimeBaseInitStructure.TIM_Period = arr;TIM_TimeBaseInitStructure.TIM_Prescaler = psc;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;             //只有高级定时器需要设置,其他定时器可以不设置。TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC1Init(TIM1, &TIM_OCInitStructure);                           //TIM1_OC1   TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);TIM_Cmd(TIM1, ENABLE);TIM_CtrlPWMOutputs(TIM1, ENABLE);                                   //高级定时器才有  必须打开
}

设置定时器1输出PWM波,定时器初始化为:

 定时器 TIM1_PWM_Init(1000 - 1, 3);     

定时器1输出18K的PWM波,此时输出的PWM波占空比是固定的,还不能随着正弦规律变化。下来利用定时器2的定时中断,在中断中改变PWM的占空比。

//APB1时钟分频为2  TIM2-7 时钟数为APB1 2倍
// Tout= (arr+1)*(psc+1) / Tclk
void TIM2_Init(u16 arr, u16 psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_TimeBaseInitStructure.TIM_Period = arr;TIM_TimeBaseInitStructure.TIM_Prescaler = psc;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;         //只有高级定时器需要设置,其他定时器可以不设置。TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2, ENABLE);
}u8 dc_cnt = 0;                            //占空比计数
//定时器2中断中改变 定时器1的占空比值
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){TIM_SetCompare1(TIM1, sinData[dc_cnt]);dc_cnt++;if(dc_cnt >= PointMax)dc_cnt = 0;TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

在定时器2中断中,依次将sinData数组中存放的正弦数据做为PWM的占空比值。由于生成的正弦数据最大值是1000,输出PWM的ARR值也是1000,那么生成的正弦值就可以直接做为占空比用。如果生成的正弦波数据和ARR值不一样,在此处需要一个转换,用比例系数调整正弦波中的最大值刚好是占空比最大值就行。
这样在定时器1输出PWM比的时候,在定时器2中断中调整PWM的占空比,这样输出的PWM波经过外部电容后,就是一个正弦波。在PA8引脚接105电容。生成的波形如下:

生成了正弦波之后,初步目的算是达到了。那么如何用生成的正弦波,和外部市电波形同步呢。
可以用过零检测电路将外部波形测出来。在每个零过点的时候输出一个脉冲。然后用中断检测这个脉冲,每次过零点到来时,在中断中将正弦波数组下标设置为0,这样每个过零点时,输出的正弦波也从0点开始输出。
外部中断代码如下:

void IO_Exti_Init(void)
{EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);EXTI_InitStructure.EXTI_Line = EXTI_Line0;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}extern u8 dc_cnt;                          //占空比计数
//10ms中断一次
void EXTI0_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line0) != RESET){dc_cnt = 0;EXTI_ClearITPendingBit(EXTI_Line0);}
}

在外部中断中将正弦波数组的下标控制值dc_cnt 清零,这样每个过零点时,输出的正弦波数据正好是第一个。
这样输出的SPWM波就可以通过中断和外部波形进行同步了。

通过波形可以看出,在过零点位置,输出正弦波也刚好在零点。
如果要调整输出正弦波数据的初始位置怎么办呢?只需要在生成正弦波数据时,设置初始位置就行。

void get_sin_tab1( u16 point, u16 maxnum )
{u16 i = 0, j = 0, k = 0;float hd = 0.0;            //弧度float fz = 0.0;        //峰值u16 tem = 0;j = point / 2;            //水平线位置 单片机没有负电压 水平线为点值数量的一半hd = PI / j;           // π/2 内每一个点对应的弧度值k = maxnum / 2;          //最大值一半for( i = 0; i < point; i++ ){fz = k * sin( hd * i ) + k;                     //起点为中间值//fz = k * sin( hd * i + PI / 2) + k;            //起点位置偏移到 π/2  起点为最大值//fz = k * sin( hd * i + PI / 2 + PI ) + k;        //起点位置偏移到 π/2 + π 起点为最小值tem = ( u16 )fz ;                     sinData[i] = tem;}
}

在生成数据是 sin( hd * i ) ,的值就是初始相位,如果要调整初始位置,只需要将括号中的值加上偏移量就行,默认的起始位置在正弦波的0度位置,如果要让初始位置在90度,那么给括号中的值在加上 π/2就可以了。

如果要让起始位置在270度时,在括号中加上 3/2π就可以了。

完整工程链接:https://download.csdn.net/download/qq_20222919/12005311

利用STM32F103单片机输出SPWM波相关推荐

  1. STM8学习笔记---利用PWM功能输出SPWM波

    要生成SPWM波,其关键是要生成一组正弦规律变化的数字,然后将数字对应成PWM输出的占空比,按照顺序输出占空比就行.生成正弦波的方法在 使用C语言产生正弦波数据 这篇博客有详细说明,这里就不说了. 本 ...

  2. STM32输出SPWM波,HAL库,cubeMX配置,滤波后输出1KHz正弦波

    SPWM波 对于功率方向,输出SPWM波是必须要掌握的 工程: stm32生成spwm代码Keil工程链接资源 引用spwm波定义: PWM波形就是指占空比可变的波形:SPWM波形是指脉冲宽度按正弦规 ...

  3. STM32F103单片机输出相位可调PWM波

    STM32定时器功能如下 通常使用的是PWM模式,可以通过PWM功能可以生成频率和占空比可调的方波信号,有时候需要生成初始相位可调的方波,PWM功能就就不能满足要求了.可以通过输出比较模式来实现. 输 ...

  4. STM32F103 PA8不能输出PWM波问题

    在调试STM32F103单片机输出PWM波时,单独测试PA8和PA11输出PWM波(也就是TIM1_CH1和TIM1_CH4),输出PWM波正常.然后用串口1输出数据时,发现一个问题,串口输出正常,但 ...

  5. STM32F103单片机生成16路PWM波

    单片机输出pwm波在项目中是恨常用的一个功能,今天就总结一下用STM32F103C8T6单片机输出16路 pwm波. 使用keil5软件,用标准库函数来实现. 首先看定时器初始化 void TIM1_ ...

  6. 如何用STC32产生SPWM波

    前言 提示:这里可以添加本文要记录的大概内容: SPWM(Sinusoidal PWM)全称是正弦脉冲宽度调制,是一种 广泛应用于电机驱动,逆变电源等领域的调制技术.SPWM波是一种按正弦规律变化的一 ...

  7. 第 40 章 呼吸灯与 SPWM 波

    40.1 呼吸灯简介 呼吸灯,就是指灯光设备的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰减,很有节奏感地一起一伏,就像是在呼吸一样. 40.2 呼吸灯与 PWM 控制原理 呼吸的特性是一种类似图指 ...

  8. STM32CubeMX——LED定时闪烁和输出PWM波

    简介 通过学习STM32CubeMX软件,掌握配置各个部件的基本方法:了解外部中断,定时器中断,PWM波产生等功能的原理,编写程序代码,实现相应的功能. 程序预期结果: LED初始设置间隔2秒闪烁一次 ...

  9. STM32单片机:定时器TIM输出PWM波

    学习32单片机过程中使用的工具:MDK Keil5 + 正点原子精英版(STM32F103ZET6)+ STM32CubeMX + HAL开发 一.PWM的产生原理 关于PWM的产生原理网上有很多教程 ...

最新文章

  1. Scrum企业实践-Leangoo敏捷工具
  2. 边缘检测与轮廓检测有什么区别?
  3. 分布式事务:两段式提交(最终一致性)
  4. discuz修改用户uid_你知道Linux中的UID和GID的含义吗
  5. android多接口请求参数,okhttp3 使用json参数post方式请求接口数据(android)
  6. 召回率和精确率(recall and precision)
  7. UI设计配色专辑,设计师应用技巧
  8. 海量数据挖掘MMDS week7: 局部敏感哈希LSH(进阶)
  9. unity打开摄像头
  10. Android基础:Android布局
  11. 一道有趣的OSPF配置实例——OSPF P2P接口妙用
  12. cocos creator 台球小游戏
  13. dell 恢复介质_在戴尔计算机上重置或重新安装Windows 10
  14. 阿里达摩院招聘 Research Intern
  15. 小白学习的sql注入
  16. 51CTO“硅谷技划”日记之一:飞向旧金山的技术集结号
  17. ODBC、JDBC和四种驱动类型
  18. SaaS是什么,目前主流的国内SAAS平台提供商有哪些?
  19. win7计算机c盘搜索不到,Win7系统如何查找C盘中的ProgramData文件夹?
  20. Linux 命令学习 -重置root密码

热门文章

  1. iframe的onload在Chrome/Opera中执行两次Bug
  2. VTP冲突的排查与解决
  3. 快速集成iOS基于RTMP的视频推流
  4. CCNA学习笔记(一)网络互联基础
  5. 进程环境之命令行参数
  6. 转载:公司招聘中不能说的秘密
  7. 【python】导入HTMLTestRunner失败---报错ModuleNotFoundError: No module named ‘HTMLTestRunner‘
  8. 【Hadoop】Hadoop1.X版本与Hadoop2.X的区别
  9. Linux服务器添加SVN用户
  10. JMeter使用命令行模式生成HTML测试报告