你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

一个关于STM32 PWM输出无信号的话题

[复制链接]
STMCU小助手 发布时间:2021-1-26 10:51
一个关于STM32PWM输出无信号的话题
近日,从ST MCU技术论坛看到一个贴子,觉得有点意思,拿过来稍作整理交流下。
发帖者问: “我利用stm32f103要做PWM輸出,利用timer1 對GPIO PE8, PE9 做輸出程式碼如下,當我將PE8,9設定為out_pp時利用示波器可以看到波形輸出,但是一設定成AF_PP時,示波器就看不到任何輸出了!所以想要請問,我下面的程式碼哪裡出錯了呢?”
从上面的繁体字和措辞不难看出发帖者极可能是港台同胞。文字信息就这么多。另外发帖者还附加了下面一些程序配置代码。
  1. void Time_init(void)
    3 b5 _3 n1 i# M' {+ s% I+ r
  2. {9 y. D! y) Q% [
  3. TIM_TimeBaseInitTypeDef TIM1_TimeBaseInitStruct;
    1 K- l- H; V# |7 ^
  4. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    : B: p- k5 p7 K9 \" f7 r$ W$ M+ d
  5. RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1,DISABLE);  O' V, k! ~- A
  6. TIM1_TimeBaseInitStruct.TIM_Prescaler = 999;
    ! O* U* b$ B; o4 f! n" O) H
  7. TIM1_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;0 K3 I# K0 K6 h; A* ~8 f
  8. TIM1_TimeBaseInitStruct.TIM_Period = 8;
    9 p/ d0 f" k! K4 t
  9. TIM1_TimeBaseInitStruct.TIM_ClockDivision = 0x0;0 W8 V  [0 |0 M8 }
  10. TIM1_TimeBaseInitStruct.TIM_RepetitionCounter = 0;) G. a6 X( G9 ~6 R# a( b
  11. TIM_TimeBaseInit(TIM1,&TIM1_TimeBaseInitStruct);" w$ E0 w  S! V) n# {
  12. TIM_ClearITPendingBit(TIM1,TIM_IT_CC1); 6 _+ A7 M, z! ?
  13. TIM_ITConfig(TIM1,TIM_IT_CC1,ENABLE); 6 M. K0 ?& K7 [3 [9 p
  14. TIM_Cmd(TIM1,ENABLE);- f2 a/ K& x8 X& N# f* F
  15. }
    9 i' q$ t+ z  l2 Z. ?
  16. - \- p/ ?* l- ^. {4 ~( d
  17. void pwm_init(void)
    & F- {6 q+ H' q
  18. {
    9 r5 y% O) ]/ }- L$ x4 M! x( J6 s
  19. TIM_OCInitTypeDef TIM_OCInitStructure;0 G- }4 C2 T' d; n. d
  20. TIM_BDTRInitTypeDef TIM_BDTRInitStructure;9 d: z( B1 j) F1 G# Q' N
  21. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    ! G0 C1 y  s4 t; H, i7 |
  22. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;' I+ n) n; X: d$ o2 w/ X# @
  23. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    9 _" a# Z3 E9 W& e: j  }
  24. TIM_OCInitStructure.TIM_Pulse = 120;
    ( R& |, i% V2 n. |
  25. TIM_OC1Init(TIM1,&TIM_OCInitStructure);5 f9 |* Z/ j& x% a% o8 r0 D
  26. TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    / n) b% a3 u, W' g8 Y

  27. 9 p' S9 w5 B) \/ L0 }
  28. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;5 `, D! E5 f. H9 A& H
  29. TIM_OCInitStructure.TIM_Pulse = 680;
      o9 X; X+ J1 ?9 g" ~! H
  30. TIM_OC2Init(TIM1, &TIM_OCInitStructure);
    ! Y, z2 I$ E& T- @8 U6 s4 L  u
  31. ; c9 w& t8 S9 B9 R4 N
  32. TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable);3 ]' T- Q( X, d! N8 g- N1 E
  33. 0 M5 S1 e3 b2 O7 T6 v" N, B
  34. TIM_ARRPreloadConfig(TIM1, ENABLE);( K6 @$ T1 N' z+ p5 W& I$ q
  35. TIM_ClearITPendingBit(TIM1, TIM_IT_CC1 );
    ' r5 M4 L- f0 ?) P
  36. + O, p- E% C7 T% x  m5 L
  37. TIM_ITConfig(TIM1,TIM_IT_CC1,ENABLE); : p$ r" r- n: U( g
  38. TIM_Cmd(TIM1, ENABLE);! w$ o' G, e; u
  39. }' e3 k+ |9 [3 F) |; f" J+ ]
  40. 6 A* g2 d/ c: J) Q
  41. void GPIO_Configuration(void)1 U1 X, k" ]/ m. Q- q9 I
  42. {
    % P+ o' w) ?. r6 F$ j
  43. GPIO_InitTypeDef g;
    $ o7 ~& |, ^& e5 {6 k% Y7 ?, t6 ?, ?
  44. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE| RCC_APB2Periph_AFIO, ENABLE); 3 p8 F$ Z! {7 o" a" r  r! a: n4 k, z
  45. 4 e+ I& @9 Y) }) ~3 J+ j- J
  46. g.GPIO_Pin = GPIO_Pin_9;
    % L: u1 B( V& P$ s
  47. g.GPIO_Mode = GPIO_Mode_AF_PP;
    / ~. @8 H' H# _. C1 S
  48. g.GPIO_Speed = GPIO_Speed_50MHz;
    + G+ F' o. x5 d) ?
  49. GPIO_Init(GPIOE, &g); ( H) m5 o8 T$ f. f: |# ]9 `

  50. + g1 Z( ~  b  d3 j8 t
  51. g.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9 ; / s6 `" W5 D7 s
  52. g.GPIO_Mode = GPIO_Mode_AF_PP;
    8 ?, I2 s3 [; _" F) @
  53. g.GPIO_Speed =GPIO_Speed_50MHz; 8 d) l# z/ A3 {& V. [3 r
  54. GPIO_Init(GPIOE, &g);
    7 z. j. o! s; j
  55. GPIO_PinRemapConfig(GPIO_PartialRemap_TIM1, ENABLE);
    9 d! C. T: t; s5 W& l0 w+ |, ]3 v4 S# ^
  56. }' N& M, j5 L" l3 f5 _6 D

  57.   b; z. W: d  X3 g# A
  58. int main(void)
    8 H. T7 ]' ^1 P) E" W( |& k& b2 b! w! {
  59. { + z9 U* k5 j' b. v* V6 x4 k
  60. RCC_Configuration(); : S1 d- U7 t+ M+ f: U  r
  61. NVIC_Configuration();% I) X2 O2 ?& B& l
  62. GPIO_Configuration();
    1 ?) r: q4 Z5 H  f4 M( {% p3 d# S
  63. Time_init();
    1 B* m) p4 r3 G$ Z1 q* D. r
  64. pwm_init();
    / @7 V( k7 H" }6 r2 Q! t
  65. while(1);
    ) A/ s0 `) o0 Z' w4 o( U. \9 X7 d
  66. }
    6 O" B4 h6 X7 D7 I# T7 V' [( r
  67. void TIM1_CC_IRQHandler(void) //Interruptroutine
    8 [# Q, N5 o3 t
  68. {
    0 g* U( e$ T  L- l9 S$ X) }
  69. if (TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET)& X% E% z; V6 u$ Y1 e
  70. {
    $ }* R8 L2 k& p3 o( @6 v
  71. TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);7 L( [- W4 V- Q5 N) v( }7 E
  72. $ t+ }( @/ T9 t, i# @
  73. GPIOE-> ODR ^= GPIO_Pin_9;5 G" e: Q& W! o% o4 q
  74. }& l+ @/ |4 `; ~: f& ~' s) i9 T
  75. }
复制代码
0 y9 ^( a" V  o9 U2 M
可以看出,发帖者的代码是基于ST 官方的传统标准外设库来写的。上面代码对TIM1的ARR、CCR1、CCR2及相关捕捉中断做了配置并使能。对PE8/PE9做了GPIO及复用配置,开启了TIM1复用脚的部分REMAP功能。针对上述问题,下面拟出几点一起交流下。

9 n9 l+ G+ Z( Y2 l. H
1、看看上面红色的捕捉中断代码。竟然发现有对GPIO口PE9的翻转操作,感觉上他是希望利用捕捉中断做GPIO翻转来实现PWM输出?
诚然,在使用有些其它品牌MCU芯片的时候,要实现PWM输出可能不太方便,需借助定时中断和GPIO翻转来实现。这个过程实现起来往往并不是很方便,也有诸多局限性。对于ST MCU,不论STM8还是STM32所有芯片都能利用内部的定时器轻松实现PWM输出。只需做些基本配置,给定信号周期和脉宽就好,无须借助CPU中断来协助实现。

$ k- _+ b' R2 J( p( ^( B/ V
2、发帖者的描述信息中没有给出完整的STM32芯片型号,不说完整芯片型号经常是个麻烦事。STM32有9大系列,几百个料号。其实不少问题是跟具体料号息息相关的。所以,如果通过邮件或网络咨询时,提供完整的信息是必须的。
1.1.jpg
上图是STM32F1系列参考手册里关于TIM1复用功能REMAP的表格。
发帖者配置了PE8/PE9却又只是做了PARTIALREMAP, 结合上图表格得知TIM1的OC1/OC2输出只能出现在PA8/PA9。不知怎么又扯到PE8/PE9了。如果用PE8、PE9,那对应的OC输出应该是OC1和OC1N这对互补输出,这跟OC1/OC2又并不一样,而且还得做FULL REMAP操作才行。也就是说,按照他的配置,在PE口是看不到TIM1_CH1/2的OC输出的,具体到这里就是不能在PE口看到PWM输出。
( s* i& d8 D( u1 M9 u
3、发帖者说当他把PE9的GPIO模式配置为OUT_PP时能看到脉冲,配置为AF_PP时又看不到,这是怎么回事呢?
在标准库里对GPIO输出模式有相关定义,这里的OUT_PP、AF_PP分别是指GPIO_MODE_OUT_PP和GPIO_Mode_AF_PP。前者指GPIO不做复用时的输出配置;后者是指GPIO做复用输出时的配置。
尽管发帖者的代码有点混乱,但他的定时器1的基本配置还是能工作的,导致捕捉中断能进入。前面提过,在捕捉中断里他做了PE9的IO翻转。作为普通GPIO口,即配置为GPIO_MODE_OUT_PP,当然能用示波器看到该脚翻转的脉冲,但这并等同于来自STM32定时器硬件实现的PWM信号,纯粹IO翻转脉冲。而当PE9被配置为GPIO_Mode_AF_PP时,意味着它要输出其它复用信号,而不是本身IO通道的信号。前面说了,按他现有配置,PWM输出是到不了PE9的,此时看不到IO翻转信号也就不难理解了。
下面是STM32F1系列GPIO管脚复用时的原理框图
1.2.jpg
  ]0 s& a; U3 {: V3 x
4、发帖者的代码混乱还有个地方,那就是关于PWM的脉宽和信号周期的设置。上面的代码里信号周期设置为8个预分频时钟,而2个通道的脉宽却配置为120和680个,即CCR比ARR大得多。按照这样的配置,即使其它有关GPIO复用及REMAP的地方配置无误,它也无法在相应管脚看到PWM跳变脉冲。因为两个通道都没有电平翻转的机会,输出一定是个固定电平。具体是高还是低跟PWM输出模式与CCR的值有关。经常有人在参考库代码基础上,机械地对个别数据一通神改,结果发现PWM出不来了。还比如,对DEADTIME参数的随意修改,也会导致同样问题。
. q) a* }$ D5 q+ D9 H# S
5、STM32F1系列是目前STM32 九大系列中推出得最早的,其有关管脚复用配置个人觉得是最啰嗦的,没有后面推出的STM32F0、F4、F3等系列的配置简洁。另外印象中STM32F1系列也是唯一没有二级加密保护的芯片。如果可能的话,在新品选型时不一定要拘泥于STM32F1,其实STM32家族中有很多性价比很好的型号可以选择。再就是对于初学者的STM32开发,建议使用STM32CUBEMX做初始化配置。尤其涉及到管脚复用和重映射的地方,操作简单快捷,不易出错。也建议尽量使用ST官方推出CUBE固件库,里面资源比传统固件库更为丰富。

% J- e$ @- ~, _3 I
6、程序代码的正确,终究离不开对原理的清晰理解。不论有多好、多方便的工具,它不论代替你对原理的理解和把握。

  `& X7 @( t7 ~: [5 i" o
文章出处: 茶话MCU
* c( v) B  i2 y' \9 o" U, \
赞 收藏 评论0 发布时间:2021-1-26 10:51

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版