历史上的今天
返回首页

历史上的今天

今天是:2025年08月01日(星期五)

正在发生

2019年08月01日 | STM32实战六 PWM加移相正交

2019-08-01 来源:eefocus

这一章编写PWM程序,使用TIM3以两个通道,完全映射到PC6和PC7,除普通PWM输出外,增加移相正交PWM功能,为后面的编码器计数模式提供信号源。


PWM.h


#ifndef __PWM__

#define __PWM__

 

extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段

#pragma diag_remark 368 //消除 warning:  #368-D: class "" defines no constructor to initialize the following:

 

#include "stm32f10x.h"

 

#pragma diag_default 368 // 恢复368号警告

}

 

#include "Timer.h"

#include "IO.h"

 

class PWM : public IO, public Timer

{

// Construction

public:

PWM();

 

// Properties

public:

 

protected:

private:

 

// Methods

public:

void setData(u16 ch, u16 nData);

void orthogonal( u16 nArr, u16 nPrescaler ); // 生成正交波型

void orthogonal2( u16 nArr, u16 nPrescaler ); // 生成反向正交波型

// Overwrite

public:

};

 

#endif

PWM.cpp 


/**

  ******************************************************************************

  * @file  PWM.cpp

  * @author  Mr. Hu

  * @version  V1.0.0 STM32F103VET6

  * @date  05/21/2019

  * @brief PWM

  * @IO

  * TIM3 定时器3

  * PC6 PWM1

  * PC7 PWM2

  * PC8 PWM预留

  * PC9 PWM预留

  ******************************************************************************

  * @remarks

  * 1KHz PWM 刻度1000

  * 只用TIM3通道1和2,对应PC6和PC7

  *

  * 参考资料:

  * https://www.cnblogs.com/zhoubatuo/p/6135103.html 有端口分配图

  * https://blog.csdn.net/qq_36554582/article/details/81239628 完整代码

  * https://blog.csdn.net/zqingyaa/article/details/86255208 分频计算方法

  */ 

 

/* Includes ------------------------------------------------------------------*/

extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段

#pragma diag_remark 368 //消除 warning:  #368-D: class "" defines no constructor to initialize the following:

 

#include "stm32f10x_tim.h"

 

#pragma diag_default 368 // 恢复368号警告

}

 

#include "PWM.h"

 

/**

  * @date 05/21/2019

  * @brief  PWM,脉宽调制

: * IO(GPIOC, GPIO_Pin_6 | GPIO_Pin_7, GPIO_Mode_AF_PP, 1)

  * 初始化PC6、PC7,GPIO_Mode_AF_PP复用输出

  * 最后一个参数1表示初始化为高电位,但实际是低电位

  * 默认产生同相PWM,频率1k,占空比调节范围0-999,用setData设置

  * 调用成员函数orthogonal时,两个输出端PC6、PC7产生正交PWM波形,占空比为50%,

  * 主要用于模拟编码输出信号。

  * orthogonal2产生反向的正交波形,用于编码器递减计数。

  * @param None

  * @retval None

  */

PWM::PWM()

: IO(GPIOC, GPIO_Pin_6 | GPIO_Pin_7, GPIO_Mode_AF_PP, 1) // GPIOx, nPin, GPIO_Mode_AF_PP 上拉, 2 输入时无效

, Timer(TIM3) // 用TIM3

{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);// 使能复用输出,不映射端口时可以不用这一句

    GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); // 把TIM3重映射到PC6-9,只用PC6-7。如果不映射,不要这一句

 

// 同相PWM,两个通道同相

TIM_TimeBaseStructure.TIM_Period = 1000-1; // 自动重装载寄存器的值 // https://blog.csdn.net/zqingyaa/article/details/86255208 分频计算方法

TIM_TimeBaseStructure.TIM_Prescaler = 72-1; // TIMX预分频的值,主频72M,刻度100,分频720,得到1kHz的PWM

TIM_TimeBaseInit(m_pTIMx, &TIM_TimeBaseStructure); // 初始化

// 设置CH1的PWM模式,输出GPC6

TIM_OCInitTypeDef  TIM_OCInitStructure; // 定义结构体

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // 选择定时器模式,TIM脉冲宽度调制模式2,两种方式是反相

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出比较极性低

TIM_OC1Init(m_pTIMx, &TIM_OCInitStructure); // 根据结构体信息进行初始化

TIM_OC1PreloadConfig(m_pTIMx, TIM_OCPreload_Enable);  // 使能定时器在CCR1上的预装载值

 

// 设置CH2的PWM模式,输出GPC7

TIM_OC2Init(m_pTIMx, &TIM_OCInitStructure); // 根据结构体信息进行初始化

TIM_OC2PreloadConfig(m_pTIMx, TIM_OCPreload_Enable);  // 使能定时器在CCR2上的预装载值

}

 

/**

  * @date 05/21/2019

  * @brief  两个输出端正交输出

  * 此函数执行后,生成占空比为50%的正交方波,不用再执行setData

  * @param nArr自动重装载寄存器的值

  * @param nPrescaler,预分频数

  * @retval None

  */

void PWM::orthogonal( u16 nArr, u16 nPrescaler )

{

// 移相PWM,两个通道正交,用于编码器模式测试。 https://www.eeworld.com.cn/mcu/2018/ic-news070140135.html

// 不用在主循环中加设置 pwm.setData(0, 3); pwm.setData(1, 1);

// 最大PWM频率 36/2 = 18M,18M时编码器计数器不工作。

// 同相PWM,两个通道同相

TIM_TimeBaseStructure.TIM_Period = nArr; // 自动重装载寄存器的值 // https://blog.csdn.net/zqingyaa/article/details/86255208 分频计算方法

TIM_TimeBaseStructure.TIM_Prescaler = nPrescaler; // TIMX预分频的值,主频72M,刻度100,分频720,得到1kHz的PWM

TIM_TimeBaseInit(m_pTIMx, &TIM_TimeBaseStructure); // 初始化

// 设置CH1的PWM模式,输出GPC6

TIM_OCInitTypeDef  TIM_OCInitStructure; // 定义结构体

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; // 选择定时器模式,TIM脉冲宽度调制模式2,两种方式是反相

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出比较极性低

TIM_OCInitStructure.TIM_Pulse = 0;    // 第一个通道不移相

TIM_OC1Init(m_pTIMx, &TIM_OCInitStructure); // 根据结构体信息进行初始化

TIM_OC1PreloadConfig(m_pTIMx, TIM_OCPreload_Enable);  // 使能定时器在CCR1上的预装载值

 

// 设置CH2的PWM模式,输出GPC7

TIM_OCInitStructure.TIM_Pulse = (nArr + 1) / 2;    // 第二个通道移相,TIM_Period减到1时翻转,原值的一半,正好正交

TIM_OC2Init(m_pTIMx, &TIM_OCInitStructure); // 根据结构体信息进行初始化

TIM_OC2PreloadConfig(m_pTIMx, TIM_OCPreload_Enable);  // 使能定时器在CCR2上的预装载值

}

 

/**

  * @date 05/21/2019

  * @brief  两个输出端正交输出,反向,用于编码器递减计数

  * 此函数执行后,生成占空比为50%的正交方波,不用再执行setData

  * @param nArr自动重装载寄存器的值

  * @param nPrescaler,预分频数

  * @retval None

  */

void PWM::orthogonal2( u16 nArr, u16 nPrescaler )

{

// 移相PWM,两个通道正交,用于编码器模式测试。 https://www.eeworld.com.cn/mcu/2018/ic-news070140135.html

// 不用在主循环中加设置 pwm.setData(0, 3); pwm.setData(1, 1);

// 最大PWM频率 36/2 = 18M,18M时编码器计数器不工作。

// 同相PWM,两个通道同相

TIM_TimeBaseStructure.TIM_Period = nArr; // 自动重装载寄存器的值 // https://blog.csdn.net/zqingyaa/article/details/86255208 分频计算方法

TIM_TimeBaseStructure.TIM_Prescaler = nPrescaler; // TIMX预分频的值,主频72M,刻度100,分频720,得到1kHz的PWM

TIM_TimeBaseInit(m_pTIMx, &TIM_TimeBaseStructure); // 初始化

// 设置CH1的PWM模式,输出GPC6

TIM_OCInitTypeDef  TIM_OCInitStructure; // 定义结构体

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; // 选择定时器模式,TIM脉冲宽度调制模式2,两种方式是反相

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 输出比较极性低

TIM_OCInitStructure.TIM_Pulse = (nArr + 1) / 2; // 第一个通道移相,TIM_Period减到1时翻转,原值的一半,正好正交

TIM_OC1Init(m_pTIMx, &TIM_OCInitStructure); // 根据结构体信息进行初始化

TIM_OC1PreloadConfig(m_pTIMx, TIM_OCPreload_Enable);  // 使能定时器在CCR1上的预装载值

 

// 设置CH2的PWM模式,输出GPC7

TIM_OCInitStructure.TIM_Pulse = 0;    //第二个通道不移相

TIM_OC2Init(m_pTIMx, &TIM_OCInitStructure); // 根据结构体信息进行初始化

TIM_OC2PreloadConfig(m_pTIMx, TIM_OCPreload_Enable);  // 使能定时器在CCR2上的预装载值

}

 

/**

  * @date 05/21/2019

  * @brief  设置占空比,正交方式时不用这个设置

  * @param ch 0通道1,否则通道2

  * @param nData 宽度0-999

  * @retval None

  */

void PWM::setData(u16 ch, u16 nData)

{

if( ch )

TIM_SetCompare2(m_pTIMx, nData); // 通道2,PC7

else

TIM_SetCompare1(m_pTIMx, nData); // 通道1,PC6

}

Main.h


#ifndef __PWM__

#define __PWM__

 

extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段

#pragma diag_remark 368 //消除 warning:  #368-D: class "" defines no constructor to initialize the following:

 

#include "stm32f10x.h"

 

#pragma diag_default 368 // 恢复368号警告

}

 

#include "Timer.h"

#include "IO.h"

 

class PWM : public IO, public Timer

{

// Construction

public:

PWM();

 

// Properties

public:

 

protected:

private:

 

// Methods

public:

void setData(u16 ch, u16 nData);

void orthogonal( u16 nArr, u16 nPrescaler ); // 生成正交波型

void orthogonal2( u16 nArr, u16 nPrescaler ); // 生成反向正交波型

// Overwrite

public:

};

 

#endif

Main.cpp


/**

  ******************************************************************************

  * @file  Main.cpp

  * @author  Mr. Hu

  * @version  V1.0.0 STM32F103VET6

  * @date  05/18/2019

  * @brief  程序入口

* @io

* TIM3 PWM

* TIM4 Encode

* TIM7 通用定时器

* ADC1 ADC

* DAC1

* DAC2

*

* PA0-PA3 ADC

* PA4 DAC1输出

* PA5 DAC2输出

* PA6 ADC预留

* PA7 ADC预留

* PA9 板载串口

* PA10 板载串口

* PA13 板载JLINK占用

* PA14 板载JLINK占用

* PA15 板载JLINK占用

*

* PB1 板载SW2

* PB3 板载JLINK占用

* PB4 板载JLINK占用,部分映像的通道1不能用,所以用了没有得映像

* PB6 编码器 A

* PB7 编码器 B

* PB8 板载CAN

* PB9 板载CAN

* PB10 板载RS485

* PB11 板载RS485

* PB13 板载LED2

* PB14 板载LED3

* PB15 板载SW3

*

* PC4 板载RS485

* PC5 板载RS485

* PC6 PWM1

* PC7 PWM2

* PC8 PWM预留

* PC9 PWM预留

******************************************************************************

  * @remarks

  *

  */ 

 

extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段

#pragma diag_remark 368 //消除 warning:  #368-D: class "" defines no constructor to initialize the following:

 

#include "stm32f10x_tim.h"

#include "stm32f10x_dac.h"

 

#pragma diag_default 368 // 恢复368号警告

}

 

#include "stm32f10x_adc.h"

#include "IO.h"

#include "Timer.h"

#include "GeneralTimer.h"

#include "BoardLED.h"

#include "PWM.h"

#include "Main.h" 

 

/**

  * @date 05/18/2019

  * @brief  主入口,主循环

  * 如果不正常运行,可能是栈设置不够 startup_stm32f10x_hd.s Stack_Size EQU 0x600

  * @param None

  * @retval None

*/

int main(void)

{

SystemInit(); // 配置系统时钟为72M

 

GeneralTimer tim(TIM2); // 通用定时器,实际用TIM7,不占用IO,但软件仿真只有1-4,所以选2

BoardLED boardLED( &tim ); // 板载LED

// 板载按键,PB1 SW2, PB2 SW3,不同的板子不一样。

IO key(GPIOC, GPIO_Pin_1 | GPIO_Pin_15, GPIO_Mode_IPU, 2); // GPIOx, nPin, GPIO_Mode_IPU 上拉, 2 输入时无效

// 使能按键滤波

//tim.inb[1].level = 1; // SW2 PB1 上拉

tim.inb[1].enable = 1; // SW2 PB1 使能

//tim.inb[15].level = 1; // SW3 PB15 上拉

tim.inb[15].enable = 1; // SW3 PB15

u32 loopCount = 0; // 主循环计数

PWM pwm;

//pwm.orthogonal( 2 - 1, 1000 - 1 );

while(1)

{

tim.loop(); // 必须放在主循环的第一行,按键滤波和上下沿微分。

// PWM

pwm.setData(0, 300); // PWM1 PC6 30%的占空比

pwm.setData(1, 700); // PWM2 PC7 70%的占空比

// LED

// 测试时间

推荐阅读

史海拾趣

爱浦电子(AIPULNION)公司的发展小趣事

在产品质量得到保障的同时,爱浦电子也积极开展市场拓展和品牌建设工作。公司积极参加国内外电子展会和技术交流活动,与众多行业伙伴建立了紧密的合作关系。此外,爱浦电子还注重品牌形象的塑造,通过统一的对外标识和全新的VI企业形象,提升了公司整体形象。这些努力使得爱浦电子在市场上逐渐树立起了良好的口碑和品牌形象。

CEVA, Inc公司的发展小趣事

CEVA公司在超低功耗技术方面取得了显著突破。公司开发的超低功耗IP包括由专用DSP与AI和其他类型的加速器组成的综合平台。这些加速器针对低功耗工作负载进行了优化,包括5G基带处理、智能视觉、语音识别、物理层处理和传感器融合等。这些技术的突破使得CEVA的产品在保持高性能的同时,能够大幅度降低功耗,满足了市场对节能设备的需求。

高博(GBG)公司的发展小趣事

随着业务的不断拓展,谷峰公司开始实施全球化战略。公司在美国、欧洲、俄罗斯和东南亚等多个国家和地区设立了代表处和代理处,构建了覆盖全球的研发和销售网络。这一布局不仅使谷峰能够更快速地响应全球市场需求,还促进了其与国际知名企业的交流与合作,为公司的技术创新和产品升级提供了有力支持。

DB Unlimited公司的发展小趣事

DB Unlimited始终将技术创新作为公司发展的核心驱动力。公司不断投入大量资源进行技术研发和创新,推出了多项具有行业领先水平的音频技术。这些技术的推出不仅提升了公司的竞争力,也推动了整个音频行业的发展。DB Unlimited的技术创新能力和成果得到了业界的广泛认可。

遨格芯微(AGM)公司的发展小趣事

作为电子行业的领先企业之一,AGM始终坚持以创新为核心竞争力。公司不断投入研发资源,加强与国际先进企业的合作与交流,积极引进和吸收新技术、新工艺和新材料。同时,AGM还注重人才培养和团队建设,打造了一支高素质、专业化的研发团队。这些努力为AGM在未来的发展中保持领先地位提供了有力保障,也为公司实现可持续发展奠定了坚实基础。

以上五个故事从不同角度展示了遨格芯微(AGM)公司在电子行业中的发展历程和取得的成就。通过不断的技术创新和市场拓展,AGM逐渐在可编程逻辑技术领域崭露头角,成为电子行业的佼佼者。未来,随着科技的不断进步和市场需求的不断变化,AGM将继续保持创新精神和敏锐的市场洞察力,为电子行业的发展贡献更多力量。

CHERRY公司的发展小趣事

随着业务的不断发展,Cherry将目光投向了更广阔的市场。1967年,随着个人计算机的普及,Cherry看到了键盘市场的巨大潜力,于是决定引进键盘生产线。不久之后,Cherry建造了世界上第一个键盘工厂,开始生产高质量的键盘产品。这一决策为Cherry公司后来的发展奠定了坚实的基础。

问答坊 | AI 解惑

wince 5.0 中文显示的问题

弄了很久了,都没有把中文显示正常,急呀! 我的过程是这样的: 1. 将locale中的default language改成中文(中国),Localize the build 选上 2. 将National Language Support(NLS)添加进来,还有Locale Specific Support -> Chinese[Simplified ...…

查看全部问答>

Warning: Unable to do imports from schanError: Can't find import 84 in ceddk.dll

前辈们好!小弟遇到一下问题 我在移植wince的时候(5.0-》6.0)遇到一下问题 是否前辈遇到此类问题 Pass 1... Warning: Unable to do imports from schannel.dll to RSAENH.dll - will late bind Warning: Unable to do imports from k.scha ...…

查看全部问答>

我的Windows CE .NET 4.2为什么不能新建连接呢?

为什么我的WinCE里面的“开始->设置->网络和拨号连接->新建连接”打开后“选择连接类型”下面打钩的地方全是灰色的呢,也就是不能选择拨号连接、直接连接等等选项...而且下面的“上一步”、“下一步”也是灰的。 怎么解决呢?? 谢谢大家~!!…

查看全部问答>

请教关于多线程的问题

  大家好,我的程序代码如下,但是编译后出了问题。   HANDLE hThread1;   DWORD dwThread1ID = 0;   int nParameter = 5;   hThread1 = CreateThread(NULL, 0, PicThread, (PVOID)nPar ...…

查看全部问答>

给串口发送数据问题

我收集了一些GPS数据在t.txt文档中,现在要将这里面的数据通过虚拟串口发给一个PC版的导航软件。 但我这发送有点问题,点一下就发送一部分数据,再点一下又发送一部分 请问下如何才能让程序自动一步一步发啊~~?请赐教 代码如下: using System ...…

查看全部问答>

我是新来的,求大侠指导~

我是一个新手,有c基础,汇编以前也学过, 现在想做嵌入式开发,不知道应该学那些东西,希望高手老师们能给我指点具体的学习里程,比如看什么书,先看什么后看什么,谢谢 感谢你`~…

查看全部问答>

如何将stm3加密

                                 在 keil 调完后,如以加密方式烧入芯片?…

查看全部问答>

免费申请MSP-EXP430G2开发板免费

免费申请MSP-EXP430G2开发板免费, 学习, 开发     http://focus.ti.com/asia/general/1007-MSP-EX430G2-reg.htm   上面是申请的链接。让我们一起来学习430吧。…

查看全部问答>

新手编程遇到问题

list p=16F1847A #include ;**************************   Temp equ 0x20   outer equ 0x21   inner equ 0x22 ;*************************** org 0x00 banksel TRISB          ...…

查看全部问答>

【视频】医疗模拟前端(AFE)参考平台介绍

医疗模拟前端(AFE)参考平台介绍 家庭医生离我们并不遥远,各种便携式医疗设备正在千家万户为我们服务,守护着大家的健康。 飞思卡尔基于Kinetis产品推出医疗模拟前端(AFE)参考平台,为开发便携式医疗器械提供了全新的解决方案,易于产品化,提高 ...…

查看全部问答>