OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 11336|回复: 11

分享STM32三个定时器三路PWM同时输入,再原样输出PWM信号,最小可以测16HZ信号。(原创)

[复制链接]

20

主题

111

帖子

2

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2011-10-18
在线时间
9 小时
发表于 2012-3-2 11:18:25 | 显示全部楼层 |阅读模式

在做完PWM输入模式后,自己有这样的想法,可不可以把捕获到的PWM信号在同一个定时器中原样输出呢,就这个问题,自己花了一天的时间去研究。怎么把读回来的频率和占空比在原来的定时器中的另一个口输出,程序在ARPE和影子寄存器中游了很久,最后反复看手册确定ARPE=0,要有事件更新时更新ARR,,这样频率更新的问题就解决了。那占空比就容易了。

程序说明:1、定时器2的PA0用于PMW输入捕获,PA2用于原样输出PWM信号。

               2、定时器3的PA6用于PMW输入捕获,PB0用于原样输出PWM信号。

               3、定时器3的PB6用于PMW输入捕获,PB8用于原样输出PWM信号。

              4、串口用于发送捕获的PWM信号的频率和占空比。

             5、目前这个程序测试过50HZ,70HZ的信号,

这里再次声明一下,如果你要捕获的PWM信号不在ARR,PSC,计算的范围内请自己先计算再使用本程序。

/******************main.c************************/

#include "sys.h" //系统子函数
#include "usart.h"//串口子函数  
#include "delay.h" //延时子函数
#include "TIMER.h"//PB9用于传输数据

int  main(void)

  Stm32_Clock_Init(9); //系统时钟设置
  delay_init(72);//延时函数初始化
  uart_init(72,9600); //串口初始化
  Timer2_Init(60000,72-1); //定时器2的PA0用于捕获,PA2用于PWM发送
  Timer3_Init(60000,72-1); //定时器3的PA6用于捕获,PB0用于PWM发送
  Timer4_Init(60000,72-1); //定时器4的PB6用于捕获,PB8用于PWM发送
  while(1)
  { 
  }
}
/*****************TIMER.c******************************/

#include "TIMER.h"
#include "delay.h"
#include "usart.h"//串口子函数
 
u16   IC1Value;
u16   IC2Value;

u16   IC3Value;
u16   IC4Value;

u16   IC5Value;
u16   IC6Value;

u16   DutyCycle1;
u16   Frequency1;

u16   DutyCycle2;
u16   Frequency2;

u16   DutyCycle3;
u16   Frequency3;


u8 flag=0;

/*定时器2*/
void Timer2_Init(u16 arr,u16 psc) //定时器2用于adc采样
{
 //此部分需手动修改 IO口设置
 RCC->APB1ENR|=1<<0;       //TIM2 时钟使能 
 RCC->APB2ENR|=1<<0;    //   
 RCC->APB2ENR|=1<<2;    //使能PORTA时钟
  
 GPIOA->CRL&=0XFFFFFFF0;//PA0 输出 
 GPIOA->CRL|=0X00000004;//复用功能输出      
 GPIOA->ODR|=1<<0;//PA0 上拉  
   
 GPIOA->CRL&=0XFFFFFF0F;//PA1 输出 
 GPIOA->CRL|=0X00000040;//复用功能输出      
 GPIOA->ODR|=1<<1;//PA1 上拉  

 GPIOA->CRL&=0XFFFFF0FF;//PA2输出 
 GPIOA->CRL|=0X00000B00;//复用功能输出      
 GPIOA->ODR|=1<<2;//PA2 上拉 

 TIM2->ARR=arr;//设定计数器自动重装值  
 TIM2->SC=psc;//预分频器不分频 

 TIM2->SMCR|=0x00D4;
  //TIM2->SMCR|= 1<<5; //MSM=1 主/从模式
  //TIM2->SMCR|= 5<<4; //TS=101 触发选择
  //TIM2->SMCR|= 4<<0; //SMS=100 复位模式
 
  //PA6 CCR1
  TIM2->CCMR1|=1<<0;//CC1S=01 选择输入端 
  TIM2->CCMR1|=3<<4; //IC1F=0011配置输入滤波器
  TIM2->CCER|=0<<1; //CC1P=0 选择有效转换边沿  上升沿有效
  TIM2->CCMR1|=0<<2; //IC1PS=00 配置输入分频
  TIM2->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中 
  //CCR2
  TIM2->CCMR1|=2<<8;//CC2S=10 选择输入端
  TIM2->CCER|=1<<5; //CC2P=1 选择有交转换边沿 下降沿有效
  TIM2->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中

  TIM2->DIER|=1<<1;   //允许更新捕获中断   
  TIM2->DIER|=1<<2;   //允许更新捕获中断
     
  //TIM2->CR1|=0x01;    //使能定时器3
  MY_NVIC_Init(1,1,TIM2_IRQChannel,1);//抢占1,子优先级3,组2

 //PWM
 TIM2->CCMR2|=7<<4;  //CH3 PWM2模式     
 TIM2->CCMR2|=1<<3; //CH3 预装载使能   
 TIM2->CCER|=1<<8;  //OC3  输出使能     
 //TIM2->CR1=0x0080;   //ARPE使能

 //TIM2->CR1|=0x01;    //使能定时器 3

 TIM2->CR1|=1<<0; //使能定时器 2
 //TIM2->CR1|=1<<7;               
}

/*定时器3*/
void Timer3_Init(u16 arr,u16 psc) //定时器3
{
 //此部分需手动修改 IO口设置 
 RCC->APB2ENR|=1<<0;    // 
 RCC->APB1ENR|=1<<1;       //TIM3 时钟使能
 RCC->APB2ENR|=1<<2;    //使能PORTA时钟
 RCC->APB2ENR|=1<<3;    //使能PORTA时钟
  
 GPIOA->CRL&=0XF0FFFFFF;//PA6 输出 
 GPIOA->CRL|=0X04000000;//复用功能输出      
 GPIOA->ODR|=1<<6;//PA6 上拉

 GPIOA->CRL&=0X0FFFFFFF;//PA7 输出 
 GPIOA->CRL|=0X40000000;//复用功能输出      
 GPIOA->ODR|=1<<7;//PA7 上拉

 GPIOB->CRL&=0XFFFFFFF0;//PB0 输出 
 GPIOB->CRL|=0X0000000B;//复用功能输出      
 GPIOB->ODR|=1<<0;//PB0上拉 
 
 TIM3->ARR=arr;//设定计数器自动重装值  
 TIM3->SC=psc;//预分频器不分频 

 TIM3->SMCR|=0x00D4;
  //TIM3->SMCR|= 1<<5; //MSM=1 主/从模式
  //TIM3->SMCR|= 5<<4; //TS=101 触发选择
  //TIM3->SMCR|= 4<<0; //SMS=100 复位模式
 
  //PA6 CCR1
  TIM3->CCMR1|=1<<0;//CC1S=01 选择输入端 
  TIM3->CCMR1|=3<<4; //IC1F=0011配置输入滤波器
  TIM3->CCER|=0<<1; //CC1P=0 选择有效转换边沿  上升沿有效
  TIM3->CCMR1|=0<<2; //IC1PS=00 配置输入分频
  TIM3->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中 
  //CCR2
  TIM3->CCMR1|=2<<8;//CC2S=10 选择输入端
  TIM3->CCER|=1<<5; //CC2P=1 选择有交转换边沿 下降沿有效
  TIM3->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中

  TIM3->DIER|=1<<1;   //允许更新捕获中断   
  TIM3->DIER|=1<<2;   //允许更新捕获中断
     
  //TIM3->CR1|=0x01;    //使能定时器3
  MY_NVIC_Init(1,1,TIM3_IRQChannel,2);//抢占1,子优先级1,组2
 //PWM
  TIM3->CCMR2|=7<<4;  //CH3 PWM2模式     
   TIM3->CCMR2|=1<<3; //CH3 预装载使能   
  TIM3->CCER|=1<<8;  //OC3  输出使能     
  //TIM3->CR1=0x0080;   //ARPE使能

  //TIM3->CR1|=0x01;    //使能定时器 3
  TIM3->CR1|=1<<0; //使能定时器 3     
}

/*定时器4*/
void Timer4_Init(u16 arr,u16 psc) //定时器4
{
 //此部分需手动修改 IO口设置 
 RCC->APB1ENR|=1<<2;     //TIM4 时钟使能
 RCC->APB2ENR|=1<<3;    //使能PORTB时钟
  
 GPIOB->CRL&=0XF0FFFFFF;//PB6 输出 
 GPIOB->CRL|=0X04000000;//复用功能输出      
 GPIOB->ODR|=1<<6;//PB6 上拉    
 
 GPIOB->CRL&=0X0FFFFFFF;//PB7 输出 
 GPIOB->CRL|=0X40000000;//复用功能输出      
 GPIOB->ODR|=1<<7;//PB7 上拉  

 GPIOB->CRH&=0XFFFFFFF0;//PB8 输出 
 GPIOB->CRH|=0X0000000B;//复用功能输出      
 GPIOB->ODR|=1<<8;//PB8 上拉  
 
 TIM4->ARR=arr;//设定计数器自动重装值  
 TIM4->SC=psc;//预分频器不分频 

 TIM4->SMCR|=0x00D4;
  //TIM3->SMCR|= 1<<5; //MSM=1 主/从模式
  //TIM3->SMCR|= 5<<4; //TS=101 触发选择
  //TIM3->SMCR|= 4<<0; //SMS=100 复位模式
 
  //PA6 CCR1
  TIM4->CCMR1|=1<<0;//CC1S=01 选择输入端 
  TIM4->CCMR1|=3<<4; //IC1F=0011配置输入滤波器
  TIM4->CCER|=0<<1; //CC1P=0 选择有效转换边沿  上升沿有效
  TIM4->CCMR1|=0<<2; //IC1PS=00 配置输入分频
  TIM4->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中 
  //CCR2
  TIM4->CCMR1|=2<<8;//CC2S=10 选择输入端
  TIM4->CCER|=1<<5; //CC2P=1 选择有交转换边沿 下降沿有效
  TIM4->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中

  TIM4->DIER|=1<<1;   //允许更新捕获中断   
  TIM4->DIER|=1<<2;   //允许更新捕获中断
     
  //TIM4->CR1|=0x01;    //使能定时器3
  MY_NVIC_Init(1,1,TIM4_IRQChannel,3);//抢占1,子优先级3,组2
 //PWM
 TIM4->CCMR2|=7<<4;  //CH3 PWM3模式     
 TIM4->CCMR2|=1<<3; //CH3 预装载使能   
 TIM4->CCER|=1<<8;  //OC3  输出使能     
 //TIM4->CR1=0x0080;   //ARPE使能 预装载

 //TIM4->CR1|=0x01;    //使能定时器 4
  TIM4->CR1|=1<<0; //使能定时器 4       
}


//定时器2中断服务程序 
void TIM2_IRQHandler(void)
{                 
 IC1Value = TIM2->CCR1;//读取CCR1也可以清CC1IF标志位
 IC2Value = TIM2->CCR2;//读取CCR1也可以清CC2IF标志位

 TIM2->CCR3 =IC1Value - IC2Value;//更新点空比
 Frequency1 = 1000000/IC1Value;
 DutyCycle1 = (IC2Value*100)/IC1Value;//占空比=(IC2Value/IC1Value)*100; 
 printf("Frequency1 = %d\r\n",Frequency1);
 printf("DutyCycle1 = %d\r\n",DutyCycle1);
}


//定时器3中断服务程序 
void TIM3_IRQHandler(void)
{                      
 IC3Value = TIM3->CCR1;//读取CCR1也可以清CC1IF标志位
 IC4Value = TIM3->CCR2;//读取CCR1也可以清CC2IF标志位
 TIM3->CCR3 =IC3Value-IC4Value;//更新点空比 
 Frequency2 = 1000000/IC3Value;
 DutyCycle2 = (IC4Value*100)/IC3Value;//占空比=(IC2Value/IC1Value)*100; 
 printf("Frequency2 = %d\r\n",Frequency2);
 printf("DutyCycle2 = %d\r\n",DutyCycle2);
 
}

//定时器4中断服务程序 
void TIM4_IRQHandler(void)
{                 
 IC5Value = TIM4->CCR1;//读取CCR1也可以清CC1IF标志位
 IC6Value = TIM4->CCR2;//读取CCR1也可以清CC2IF标志位
 TIM4->CCR3 =IC5Value -IC6Value;//更新点空比 
 Frequency3 = 1000000/IC5Value;
 DutyCycle3 = (IC6Value*100)/IC5Value;//占空比=(IC2Value/IC1Value)*100; 
 printf("Frequency3 = %d\r\n",Frequency3);
 printf("DutyCycle3 = %d\r\n",DutyCycle3);
}

 

 

/*******************TIMER.h********************************/

#ifndef  __TIMER_H__
#define  __TIMER_H__

#include "stm32f10x_lib.h"
#include "sys.h"

#define  Pwm_port PBout(9)
void  Pwm_port_Init(void);
void Timer3_Init(u16 arr,u16 psc);
void Timer2_Init(u16 arr,u16 psc);
void Timer4_Init(u16 arr,u16 psc); //定时器4
#endif

 

 

 

 

 

?成功只是最后一小步的坚持!
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-3-2 11:44:26 | 显示全部楼层
你这样一个定时器同时捕获,同时又输出,不会有问题?
 我怀疑会有问题。因为一个定时器只有一个计数器,你用这个计数器来统计外部脉宽了,再去控制PWM输出,难免有问题。
 与其这样做,还不如在中断里面对IO取反,来的更简单点吧?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

20

主题

111

帖子

2

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2011-10-18
在线时间
9 小时
 楼主| 发表于 2012-3-2 11:59:12 | 显示全部楼层
我开始也是这么想的,是只有一个计数器,不过因为输入的和输出的是一样的频率,所以不会冲突,经过测试是可以还原波型的。你可以试一下。
?成功只是最后一小步的坚持!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2012-3-2 12:39:35 | 显示全部楼层
回复【3楼】suqingxiao:
---------------------------------
直接在中断里面IO取反即可吧。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

20

主题

111

帖子

2

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2011-10-18
在线时间
9 小时
 楼主| 发表于 2012-3-2 14:12:15 | 显示全部楼层
取反也可以,不过要两次中断,就是IC1和IC2都要中断一次,IC2当是占空比是先中断,IC1是频率值后中断。不过因为PWM是硬件资源,用上面的方法速度会快很多。你不用去考虑太多输入信号。
?成功只是最后一小步的坚持!
回复 支持 反对

使用道具 举报

20

主题

111

帖子

2

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2011-10-18
在线时间
9 小时
 楼主| 发表于 2012-3-2 14:12:50 | 显示全部楼层
我上面的做法也是有人问过的,也是为了方便。
?成功只是最后一小步的坚持!
回复 支持 反对

使用道具 举报

2

主题

4

帖子

0

精华

新手入门

积分
32
金钱
32
注册时间
2012-4-18
在线时间
0 小时
发表于 2012-4-18 14:10:35 | 显示全部楼层
你好!怎么和您联系啊?我想具体问问关于PWM的问题!谢谢!
回复 支持 反对

使用道具 举报

20

主题

111

帖子

2

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2011-10-18
在线时间
9 小时
 楼主| 发表于 2012-4-26 11:16:17 | 显示全部楼层
QQ 345415244 请注STM32
?成功只是最后一小步的坚持!
回复 支持 反对

使用道具 举报

5

主题

20

帖子

0

精华

初级会员

Rank: 2

积分
60
金钱
60
注册时间
2013-8-23
在线时间
0 小时
发表于 2013-10-13 20:28:06 | 显示全部楼层
呵呵,LZ有没有尝试过用库函数改写这个,初学者现在还只是用库函数呢
细水长流
回复 支持 反对

使用道具 举报

71

主题

467

帖子

0

精华

高级会员

Rank: 4

积分
800
金钱
800
注册时间
2011-11-18
在线时间
5 小时
发表于 2013-10-14 08:15:25 | 显示全部楼层
搞介个最好用CPLD,50M的精度,妥妥的。一片STM带一片CPLD,很经济,一份麦当鱼的价格。
我的工作就是天天在玩
回复 支持 反对

使用道具 举报

71

主题

467

帖子

0

精华

高级会员

Rank: 4

积分
800
金钱
800
注册时间
2011-11-18
在线时间
5 小时
发表于 2013-10-14 08:17:31 | 显示全部楼层
呃,习惯了寄存器,看不懂库函数,咋和楼上的相反咧。
我的工作就是天天在玩
回复 支持 反对

使用道具 举报

20

主题

102

帖子

0

精华

初级会员

Rank: 2

积分
183
金钱
183
注册时间
2013-7-17
在线时间
30 小时
发表于 2014-10-24 09:22:19 | 显示全部楼层
回复【11楼】chinafox:
---------------------------------
看不懂寄存器啊~~~怎么办~~~~现在正需要用这个~~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-30 06:42

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表