历史上的今天
今天是: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 " #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 " #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 " #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 " #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 // 测试时间
下一篇:STM32实战七 数字滤波
史海拾趣
|
弄了很久了,都没有把中文显示正常,急呀! 我的过程是这样的: 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 ...… 查看全部问答> |
|
我是一个新手,有c基础,汇编以前也学过, 现在想做嵌入式开发,不知道应该学那些东西,希望高手老师们能给我指点具体的学习里程,比如看什么书,先看什么后看什么,谢谢 感谢你`~… 查看全部问答> |
|
免费申请MSP-EXP430G2开发板免费, 学习, 开发 http://focus.ti.com/asia/general/1007-MSP-EX430G2-reg.htm 上面是申请的链接。让我们一起来学习430吧。… 查看全部问答> |
|
医疗模拟前端(AFE)参考平台介绍 家庭医生离我们并不遥远,各种便携式医疗设备正在千家万户为我们服务,守护着大家的健康。 飞思卡尔基于Kinetis产品推出医疗模拟前端(AFE)参考平台,为开发便携式医疗器械提供了全新的解决方案,易于产品化,提高 ...… 查看全部问答> |




